package com.bestcem.xm.deliver.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bestcem.bp.xm.user.grpc.v1.services.Group;
import com.bestcem.bp.xm.user.grpc.v1.services.GroupLevel;
import com.bestcem.bp.xm.user.grpc.v1.services.GroupNode;
import com.bestcem.bp.xm.user.grpc.v1.services.OrgCustomSetting;
import com.bestcem.xm.common.core.constant.AppResourceErrorCode;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.domain.web.XmResultJson;
import com.bestcem.xm.common.core.exception.BusinessException;
import com.bestcem.xm.common.core.oss.enums.OssPathEnum;
import com.bestcem.xm.common.core.uitls.ExcelExportUtil;
import com.bestcem.xm.common.core.uitls.SignUtil;
import com.bestcem.xm.component.mybatis.base.ResultPage;
import com.bestcem.xm.component.mybatis.service.impl.XmServiceImpl;
import com.bestcem.xm.component.security.context.SecurityContextHolder;
import com.bestcem.xm.componet.filestorage.service.StorageService;
import com.bestcem.xm.componet.redis.service.RedisService;
import com.bestcem.xm.deliver.controller.param.SurveyDetailParam;
import com.bestcem.xm.deliver.controller.v2.param.*;
import com.bestcem.xm.deliver.controller.v2.vo.DedicatedDeliverVo;
import com.bestcem.xm.deliver.controller.v2.vo.DeliverDisturbRuleVo;
import com.bestcem.xm.deliver.controller.v2.vo.SurveyDeliverPageVo;
import com.bestcem.xm.deliver.controller.vo.ExcelDeliverLinkVo;
import com.bestcem.xm.deliver.controller.vo.ExcelDeliverVo;
import com.bestcem.xm.deliver.convert.DeliverConvertMapper;
import com.bestcem.xm.deliver.entity.*;
import com.bestcem.xm.deliver.enums.DeliverDefaultTypeEnum;
import com.bestcem.xm.deliver.enums.DeliverStatusEnum;
import com.bestcem.xm.deliver.enums.DeliverTtypeEnum;
import com.bestcem.xm.deliver.enums.RespondentStatusEnum;
import com.bestcem.xm.deliver.grpc.client.AwardGrpcClient;
import com.bestcem.xm.deliver.grpc.client.QdesGrpcClient;
import com.bestcem.xm.deliver.grpc.client.SurveyGrpcClient;
import com.bestcem.xm.deliver.grpc.client.UserGrpcClient;
import com.bestcem.xm.deliver.grpc.client.dto.AwardDto;
import com.bestcem.xm.deliver.grpc.client.dto.OrgInfoDto;
import com.bestcem.xm.deliver.grpc.client.dto.SurveyRespondentDto;
import com.bestcem.xm.deliver.mapper.*;
import com.bestcem.xm.deliver.mq.dto.StatusDto;
import com.bestcem.xm.deliver.mq.send.biz.DeliverMessageSendService;
import com.bestcem.xm.deliver.service.DeliverDeliverService;
import com.bestcem.xm.deliver.service.ServiceSequenceService;
import com.bestcem.xm.deliver.util.CommonUtil;
import com.bestcem.xm.deliver.util.PageUtil;
import com.bestcem.xm.deliver.util.ZipUtil;
import com.bestcem.xm.qdes.grpc.v1.services.Project;
import com.bestcem.xm.qdes.grpc.v1.services.ProjectEntry;
import com.bestcem.xm.qdes.grpc.v1.services.ProjectNoDefault;
import com.bestcem.xm.qdes.grpc.v1.services.ProjectStatus;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import com.google.protobuf.StringValue;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static com.bestcem.xm.deliver.enums.CustomApiDeliverStatusEnum.CUSTOMAPIRECORD_STATUS_SENDING;
import static com.bestcem.xm.deliver.enums.CustomApiDeliverStatusEnum.CUSTOMAPIRECORD_STATUS_SUCCESS;
import static com.bestcem.xm.deliver.enums.DeliverChannelEnum.DELIVER_CHANNEL_QRCODE;
import static com.bestcem.xm.deliver.enums.DeliverSurveyDataSurveyStatusEnum.DELIVERSURVEYDATA_SURVEY_STATUS_SUBMIT;
import static com.bestcem.xm.deliver.enums.DeliverSurveyDataSurveyStatusEnum.DELIVERSURVEYDATA_SURVEY_STATUS_UNANSWER;
import static com.bestcem.xm.deliver.enums.DeliverTtypeEnum.*;
import static com.bestcem.xm.deliver.enums.DisturbRuleOpTypeEnum.DISTURBRULE_OP_TYPE_DELIVER;
import static com.bestcem.xm.deliver.enums.DisturbRulePeriodTypeEnum.*;
import static com.bestcem.xm.deliver.enums.RespondentStatusEnum.RESPONDENT_STATUS_BEGIN;
import static com.bestcem.xm.deliver.enums.RespondentStatusEnum.RESPONDENT_STATUS_FINISH;
import static com.bestcem.xm.deliver.grpc.v1.services.DefaultDeliverStoreStatus.DEFAULTDELIVERSTORE_STATUS_ACTIVATE;
import static com.bestcem.xm.deliver.grpc.v1.services.DeliverAwardUseType.DELIVER_AWARD_USE_TYPE_UNUSE;
import static com.bestcem.xm.deliver.grpc.v1.services.DeliverAwardUseType.DELIVER_AWARD_USE_TYPE_USE;
import static com.bestcem.xm.deliver.grpc.v1.services.DeliverChannel.DELIVER_CHANNEL_DEDICATED;
import static com.bestcem.xm.deliver.grpc.v1.services.DeliverScenes.DELIVER_SCENES_DEDICATED;
import static com.bestcem.xm.deliver.grpc.v1.services.DeliverScenes.DELIVER_SCENES_QRCODE;
import static com.bestcem.xm.deliver.grpc.v1.services.DeliverStatus.*;
import static com.bestcem.xm.deliver.grpc.v1.services.DeliverTtype.DELIVER_TTYPE_DEDICATED;
import static com.bestcem.xm.deliver.grpc.v1.services.DeliverTtype.DELIVER_TTYPE_DEFAULT_STORE;
import static com.bestcem.xm.deliver.grpc.v1.services.DisturbRuleTtype.*;
import static com.bestcem.xm.deliver.grpc.v1.services.SMSRecordStatus.SMSRECORD_STATUS_SENDING;
import static com.bestcem.xm.deliver.grpc.v1.services.SMSRecordStatus.SMSRECORD_STATUS_SUCCESS;
import static com.bestcem.xm.deliver.grpc.v1.services.SurveyRegionStatus.SURVEYREGION_STATUS_ACTIVATE;
import static com.bestcem.xm.deliver.grpc.v1.services.SurveyRegionTtype.SURVEYREGION_TTYPE_STORE;
import static com.bestcem.xm.deliver.util.DeliverConstant.*;
import static com.bestcem.xm.qdes.grpc.v1.services.ProjectStatus.*;
import static com.bestcem.xm.survey.grpc.v1.services.RespondentSource.RESPONDENT_SOURCE_DEDICATED;
import static com.bestcem.xm.survey.grpc.v1.services.RespondentSource.RESPONDENT_SOURCE_QRCODE;

/**
 * @author been
 * @description 针对表【ds_deliver_deliver】的数据库操作Service实现
 * @createDate 2022-11-07 13:49:46
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DeliverDeliverServiceImpl extends XmServiceImpl<DeliverDeliverMapper, DeliverDeliver>
        implements DeliverDeliverService, InitializingBean {
    private static final String SEPARATOR = "/";
    private static final String[] X36_ARRAY = "0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z".split(",");
    private static final String ROOT_FILE_PATH = System.getProperty("user.dir");
    /**
     * BiConsumer<T,U>
     * 代表了一个接受两个输入参数的操作，并且不返回任何结果
     * <p>
     * BiFunction<T,U,R>
     * 代表了一个接受两个输入参数的方法，并且返回一个结果
     * <p>
     * Function<T,R>
     * 接受一个输入参数，返回一个结果
     */
    private final ConcurrentHashMap<DeliverTtypeEnum, Function<String, List<String>>> getDeliverMemberIdListFunctionMap = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<DeliverTtypeEnum, Function<String, Object>> getBaseAndMemberIdListByDeliverIdFunctionMap = new ConcurrentHashMap<>();

    private @NonNull DeliverConvertMapper deliverConvertMapper;
    //private @NonNull ObjectMapper objectMapper;

    private @NonNull QdesGrpcClient qdesGrpcClient;
    private @NonNull UserGrpcClient userGrpcClient;
    private @NonNull AwardGrpcClient awardGrpcClient;
    private @NonNull SurveyGrpcClient surveyGrpcClient;

    private @NonNull ServiceSequenceService serviceSequenceService;
    private @NonNull RedisService redisService;
    //private @NonNull OssService ossService;
    private @NonNull StorageService storageService;
    private @NonNull DeliverMessageSendService deliverMessageSendService;

    private @NonNull DeliverWechatDeliverMapper wechatDeliverMapper;
    private @NonNull DeliverSmsDeliverMapper smsDeliverMapper;
    private @NonNull DeliverEmailDeliverMapper emailDeliverMapper;
    private @NonNull DeliverCustomApiDeliverMapper customApiDeliverMapper;
    private @NonNull DeliverDefaultDeliverStoreMapper deliverStoreMapper;
    private @NonNull DeliverSurveyDeliverMapper deliverSurveyDeliverMapper;
    private @NonNull DeliverDisturbRuleMapper deliverDisturbRuleMapper;
    private @NonNull DeliverDedicatedDeliverMapper dedicateDdeliverMapper;
    private @NonNull DeliverCustomDeliverMapper customDeliverMapper;
    private @NonNull DeliverSampleDeliverMapper sampleDeliverMapper;
    private @NonNull DeliverWechatmpDeliverMapper wechatmpDeliverMapper;
    private @NonNull DeliverSmsDeliverMemberIdMapper smsDeliverMemberIdMapper;
    private @NonNull DeliverSmsTemplateMapper smsTemplateMapper;
    private @NonNull DeliverSmsRecordMapper smsRecordMapper;
    private @NonNull DeliverEmailDeliverMemberIdMapper emailDeliverMemberIdMapper;
    private @NonNull DeliverEmailRecordMapper emailRecordMapper;
    private @NonNull DeliverCustomDeliverNodisturbMemberIdMapper customDeliverNodisturbMemberIdMapper;
    private @NonNull DeliverCustomDeliverMemberIdMapper customDeliverMemberIdMapper;
    private @NonNull DeliverCustomApiRecordMapper customApiRecordMapper;
    private @NonNull DeliverWechatmpDeliverMemberIdMapper wechatmpDeliverMemberIdMapper;
    private @NonNull DeliverWechatmpRecordMapper wechatmpRecordMapper;
    private @NonNull DeliverSurveyRegionMapper surveyRegionMapper;
    private @NonNull DeliverDisturbConfigMapper disturbConfigMapper;
    private @NonNull DeliverDeliverSurveyDataMapper deliverSurveyDataMapper;
    private @NonNull DeliverCustomLinkDeliverMapper linkDeliverMapper;
    private @NonNull DeliverExtDeliverMappingMapper extDeliverMappingMapper;

    @Value("${app.deliver.survey.system.protocol:http}")
    private String serverProtocol;
    @Value("${app.deliver.survey.system.uri}")
    private String surveySystemUri;
    @Value("${app.deliver.survey.system.url}")
    private String surveySystemUrl;
    @Value("${app.deliver.secret}")
    private String deliverSecret;

    @Value("${url.wjlink_long_to_short_url}")
    private String longToShortUrl;

    private static String tenTo36(int num) {
        StringBuilder sBuffer = new StringBuilder();
        if (num == 0) {
            sBuffer.append("0");
        }
        while (num > 0) {
            sBuffer.append(X36_ARRAY[num % 36]);
            num = num / 36;
        }
        return sBuffer.reverse().toString();
    }

    @Override
    public List<JSONObject> getGroupLevel(String rootGroupId) {
        Group group = userGrpcClient.getGroup(rootGroupId);
        String levelId = group == null ? null : group.getLevelId();
        List<JSONObject> levels = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            if (StringUtils.isEmpty(levelId)) {
                break;
            }
            GroupLevel groupLevel = userGrpcClient.getGroupLevel(levelId);
            if (groupLevel != null) {
                JSONObject levelJson = new JSONObject();
                levelJson.put("id", groupLevel.getId());
                levelJson.put("title", groupLevel.getName());
                levels.add(levelJson);
                levelId = groupLevel.getChildrenId();
            } else {
                levelId = null;
            }
        }
        return levels;
    }

    @Override
    public String getDeliverSecret() {
        return deliverSecret;
    }

    @Override
    public <T> String exportWithDynamicData(String fileName, List<List<List<String>>> head, List<List<List<T>>> exportDataList, List<String> sheetNameList, HttpServletResponse response, String ossPath) {
        File exportFile = ExcelExportUtil.exportWithDynamicData2LocalDir(fileName, head, exportDataList, sheetNameList);
        //上传到oss
        String url = storageService.upload(exportFile, Objects.requireNonNull(exportFile).getName(), ossPath, new Dict()).getUrl();
        ExcelExportUtil.downloadTemplate(exportFile.getPath(), fileName, ExcelExportUtil.XLSX_SUFFIX, response);
        exportFile.delete();
        return url;
    }

    // 根据问卷编号及层级树打包所有二维码
    private String tarSurveyGroupTreeQrcodes(String orgId, Project survey, DeliverDeliver deliver, GroupNode groupTree,
                                             JSONObject storeLevel, String search, List<String> specGroups,
                                             DeliverDefaultDownloadParam param) {
        String zipName;
        zipName = survey.getTitle() + "_" + deliver.getName() + "_"
                + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH_mm_ss"));
        // 替换文件名中的特殊字符为“_”
        zipName = CommonUtil.fileNameFormat(zipName);
        Boolean full = param.getFull();
        String qrcodPath = ROOT_FILE_PATH + File.separator + "qrcode";
        String tmpPath = qrcodPath + File.separator + zipName;
        /*String filePath = ossService.getFilePrefix() + OssPathEnum.QRCODE.getPath() + survey.getId()
                + SEPARATOR + deliver.getId();*/
        String filePath = storageService.getPropertiesValueBy(null, "base-path") + OssPathEnum.QRCODE.getPath() + survey.getId()
                + SEPARATOR + deliver.getId();
        File file1 = new File(tmpPath);
        if (!file1.exists()) {
            file1.mkdirs();
        }

        List<DeliverDefaultDeliverStore> defaultDeliverStores = new ArrayList<>();
        if (deliver.getTtype() == DELIVER_TTYPE_DEFAULT_STORE.getNumber()) {
            if (full) {
                if (StringUtils.isEmpty(search)) {
                    LambdaQueryWrapper<DeliverDefaultDeliverStore> queryWrapper = Wrappers.lambdaQuery();
                    queryWrapper.eq(DeliverDefaultDeliverStore::getSurveyId, survey.getId())
                            .eq(DeliverDefaultDeliverStore::getTtype, 1)
                            .eq(DeliverDefaultDeliverStore::getStatus, DEFAULTDELIVERSTORE_STATUS_ACTIVATE.getNumber());
                    defaultDeliverStores = deliverStoreMapper.selectList(queryWrapper);
                } else {
                    LambdaQueryWrapper<DeliverDefaultDeliverStore> queryWrapper = Wrappers.lambdaQuery();
                    queryWrapper.eq(DeliverDefaultDeliverStore::getSurveyId, survey.getId())
                            .eq(DeliverDefaultDeliverStore::getTtype, 1)
                            .eq(DeliverDefaultDeliverStore::getStatus, DEFAULTDELIVERSTORE_STATUS_ACTIVATE.getNumber())
                            .like(DeliverDefaultDeliverStore::getTitle, search);
                    defaultDeliverStores = deliverStoreMapper.selectList(queryWrapper);
                    full = false;
                }
            } else {
                LambdaQueryWrapper<DeliverDefaultDeliverStore> queryWrapper = Wrappers.lambdaQuery();
                queryWrapper.in(DeliverDefaultDeliverStore::getId, param.getDefaultDeliverStoreIdList())
                        .eq(DeliverDefaultDeliverStore::getStatus, DEFAULTDELIVERSTORE_STATUS_ACTIVATE.getNumber());
                defaultDeliverStores = deliverStoreMapper.selectList(queryWrapper);
            }
            List<ExcelDeliverLinkVo> excelDeliverDTOS = new ArrayList<>();
            String fileName = tmpPath + File.separator + "组织链接.xlsx";
            for (DeliverDefaultDeliverStore deliverStore : defaultDeliverStores) {
                ExcelDeliverLinkVo deliverDTO = new ExcelDeliverLinkVo();
                deliverDTO.setTitle(deliverStore.getTitle());
                deliverDTO.setLink(deliverStore.getLink());
                deliverDTO.setCreatedAt(deliverStore.getCreatedDt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                excelDeliverDTOS.add(deliverDTO);
            }
            ExcelExportUtil.createExcelStream(excelDeliverDTOS, ExcelDeliverLinkVo.class, fileName);
        } else if (deliver.getTtype() == DELIVER_TTYPE_REGION.getValue()) {
            // 层级投放下载包含失效门店
            LambdaQueryWrapper<DeliverSurveyRegion> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(DeliverSurveyRegion::getDeliverId, deliver.getId());
            if (CollectionUtils.isNotEmpty(specGroups)) {
                queryWrapper.in(DeliverSurveyRegion::getGroupId, specGroups);
            }
            List<DeliverSurveyRegion> surveyRegions = surveyRegionMapper.selectList(queryWrapper);
            List<ExcelDeliverLinkVo> excelDeliverDTOS = new ArrayList<>();
            String fileName = tmpPath + File.separator + "组织链接.xlsx";
            for (DeliverSurveyRegion sDeliver : surveyRegions) {
                ExcelDeliverLinkVo deliverDTO = new ExcelDeliverLinkVo();
                deliverDTO.setTitle(sDeliver.getTitle());
                deliverDTO.setLink(sDeliver.getLink());
                deliverDTO.setCreatedAt(sDeliver.getCreatedDt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                excelDeliverDTOS.add(deliverDTO);
            }
            ExcelExportUtil.createExcelStream(excelDeliverDTOS, ExcelDeliverLinkVo.class, fileName);
        }

        if (!full) {
            for (DeliverDefaultDeliverStore dDliver : defaultDeliverStores) {
                String deliverCode = dDliver.getCode();
                String fileName = CommonUtil.fileNameFormat(deliverCode + "@" + dDliver.getTitle() + ".png");

                String path = filePath + SEPARATOR + fileName;
                //File fileDeliver = new File(tmpPath + SEPARATOR + fileName);
                //ossService.downloadFile(path, fileDeliver);
                //storageService.downloadFile(path, fileDeliver.getPath());
                if (!storageService.exists(storageService.getPropertiesValueBy(null, "domain") + path)) {
                    //if (!fileDeliver.exists() || fileDeliver.length() == 0) {
                    Map<String, String> keyMap = new HashMap<>();
                    if (deliver.getAwardUseType() == DELIVER_AWARD_USE_TYPE_USE.getNumber()) {
                        keyMap.put("awardId", deliver.getAwardId());
                    }
                    keyMap.put("ded_code", deliverCode);
                    keyMap.put("checkCode", dDliver.getId());
                    keyMap.put("checkSubmit", "1");
                    String content = getSurveyGroupDwzUrl(orgId, survey.getId(), deliver.getId(), keyMap);
                    String srcUrl = createSurveyGroupQrcodes(orgId, survey.getId(), deliver.getId(), content, fileName, true);
                    File sourceFile = new File(srcUrl);
                    try {
                        FileUtils.copyFile(sourceFile, new File(tmpPath + File.separator + fileName));
                    } catch (IOException e) {
                        throw new BusinessException("拷贝文件失败");
                    }
                    sourceFile.delete();
                }
            }

            ZipUtil.zipHutool(tmpPath, qrcodPath + File.separator + zipName + ".zip");
            File zipFile = new File(qrcodPath + File.separator + zipName + ".zip");
            /*String url = ossService.uploadSimpleFile2(zipFile, zipName + ".zip", OssPathEnum
                    .QRCODE.getPath() + survey.getId() + SEPARATOR + deliver.getId() + SEPARATOR, 0, null, "", OssPathEnum.DELIVER);*/
            String url = storageService.upload(zipFile, zipName + ".zip", OssPathEnum
                    .QRCODE.getPath() + survey.getId() + SEPARATOR + deliver.getId() + SEPARATOR, new Dict()).getUrl();
            zipFile.delete();
            FileUtil.del(file1);
            //if (file1.listFiles() != null) {
            //    for (File file : file1.listFiles()) {
            //        file.delete();
            //    }
            //}
            //file1.delete();
            return url;
        }

        if (groupTree != null) {
            parseNode(orgId, groupTree, /*new ArrayList<>(),*/ storeLevel, filePath, tmpPath, survey, deliver, specGroups);
        }

        String[] fileList = file1.list();
        if (fileList == null || fileList.length <= 1) {
            String fileName = "通用.png";
            String path = filePath + SEPARATOR + fileName;
            //File fileDeliver = new File(tmpPath + SEPARATOR + fileName);
            //ossService.downloadFile(path, fileDeliver);
            //storageService.downloadByUrl(storageService.getPropertiesValueBy(null, "domain") + path).file(fileDeliver);
            if (!storageService.exists(storageService.getPropertiesValueBy(null, "domain") + path)) {
                //if (!fileDeliver.exists() || fileDeliver.length() == 0) {
                String content = getSurveyGroupDwzUrl(orgId, survey.getId(), deliver.getId(), new HashMap<>());
                String srcUrl = createSurveyGroupQrcodes(orgId, survey.getId(), deliver.getId(), content, fileName, true);
                File sourceFile = new File(srcUrl);
                try {
                    FileUtils.copyFile(sourceFile, new File(tmpPath + File.separator + fileName));
                } catch (IOException e) {
                    throw new BusinessException("拷贝文件失败");
                }
                sourceFile.delete();
            }
        }
        ZipUtil.zipHutool(tmpPath, qrcodPath + File.separator + zipName + ".zip");
        File zipFile = new File(qrcodPath + File.separator + zipName + ".zip");
        /*String url = ossService.uploadSimpleFile2(zipFile, zipName + ".zip", OssPathEnum
                .QRCODE.getPath() + survey.getId() + SEPARATOR + deliver.getId() + SEPARATOR, 0, null, "", OssPathEnum.DELIVER);*/
        String url = storageService.upload(zipFile, zipName + ".zip", OssPathEnum
                .QRCODE.getPath() + survey.getId() + SEPARATOR + deliver.getId() + SEPARATOR, new Dict()).getUrl();
        zipFile.delete();
        FileUtil.del(file1);
        //if (file1.listFiles() != null) {
        //    for (File file : file1.listFiles()) {
        //        file.delete();
        //    }
        //}
        //file1.delete();
        return url;
    }

    private void parseNode(String orgId, GroupNode node, /*List<GroupNode> parentNodeList,*/ JSONObject storeLevel,
                           String filePath, String tmpPath, Project survey, DeliverDeliver deliver, List<String> specGroups) {
        /*List<GroupNode> allNodeList = new ArrayList<>(parentNodeList);
        allNodeList.add(node);*/
        if (node.getLevelId().equals(storeLevel.getString("id"))) {
            String fileName = CommonUtil.fileNameFormat(node.getCode() + "@" + node.getName() + ".png");
            String path = filePath + SEPARATOR + fileName;
            //File fileDeliver = new File(tmpPath + SEPARATOR + fileName);
            //ossService.downloadFile(path, fileDeliver);
            //storageService.downloadByUrl(storageService.getPropertiesValueBy(null, "domain") + path).file(fileDeliver);

            if (CollectionUtils.isEmpty(specGroups) || specGroups.contains(node.getId())) {
                if (!storageService.exists(storageService.getPropertiesValueBy(null, "domain") + path)) {
                    //if (!fileDeliver.exists() || fileDeliver.length() == 0) {
                    Map<String, String> keyMap = new HashMap<>();
                    keyMap.put("code", node.getCode());
                    String content = getSurveyGroupDwzUrl(orgId, survey.getId(), deliver.getId(), keyMap);
                    String srcUrl = createSurveyGroupQrcodes(orgId, survey.getId(), deliver.getId(), content, fileName, true);
                    File sourceFile = new File(srcUrl);
                    try {
                        FileUtils.copyFile(sourceFile, new File(tmpPath + File.separator + fileName));
                    } catch (IOException e) {
                        throw new BusinessException("拷贝文件失败");
                    }
                    sourceFile.delete();
                }
            }
        } else {
            for (GroupNode childrenNode : node.getChildrenList()) {
                parseNode(orgId, childrenNode, /*parentNodeList,*/ storeLevel, filePath, tmpPath, survey, deliver, specGroups);
            }
        }

    }

    private void addDedicateddeliver(Integer nums, CreateDeliverParam param, Project project, DeliverDeliver deliver) {
        OrgInfoDto org = userGrpcClient.getOrganization(param.getOrgId());
        if (org == null) {
            throw new BusinessException(String.format("user.Organization[id=%s] not found", param.getOrgId()));
        }
        //可以优化成mq处理
        for (int i = 0; i < nums; i++) {
            createDedicateddeliverList(param.getOrgId(), project, deliver, org);
        }
        // 创建通用.png
        String content = getSurveyGroupDwzUrl(deliver.getOrgId(), project.getId(), deliver.getId(), new HashMap<>());
        createSurveyGroupQrcodes(deliver.getOrgId(), project.getId(), deliver.getId(), content, "通用.png", false);
    }

    private void createDedicateddeliverList(String orgId, Project project, DeliverDeliver deliver, OrgInfoDto org) {
        // 创建专用链接投放的具体记录
        Integer num = serviceSequenceService.getSequence("ded_code_seq", 1);
        String dedCode = tenTo36(num);
        Map<String, String> kwargs = new HashMap<>();
        if (deliver.getAwardUseType() == DELIVER_AWARD_USE_TYPE_USE.getNumber()) {
            kwargs.put("awardId", deliver.getAwardId());
        }
        String deliverId = deliver.getId();
        Integer status = SURVEYREGION_STATUS_ACTIVATE.getNumber();
        String checkCode = new ObjectId().toString();
        String link = getSurveyDedicatedDwzUrl(orgId, project.getId(), deliverId, dedCode, checkCode, org, kwargs);

        DeliverDedicatedDeliver params = new DeliverDedicatedDeliver();
        params.setId(checkCode);
        params.setOrgId(orgId);
        params.setDeliverId(deliverId);
        params.setCode(dedCode);
        params.setSurveyId(project.getId());
        params.setStatus(status);
        params.setLink(link);
        createDedicatedDeliverWithInfo(params, project, checkCode, org, kwargs);
    }

    // 创建对象时同步创建对应二维码
    private void createDedicatedDeliverWithInfo(DeliverDedicatedDeliver params, Project project, String checkCode,
                                                OrgInfoDto org, Map<String, String> kwargs) {
        createDedicatedQrcodes(params.getOrgId(), project, params.getDeliverId(), params.getCode(), params.getLink(),
                checkCode, org, kwargs);
        createDedicatedDeliver(params);
    }

    private void createDedicatedDeliver(DeliverDedicatedDeliver dedicateDdeliver) {
        dedicateDdeliverMapper.insert(dedicateDdeliver);
    }

    private void createDedicatedQrcodes(String orgId, Project survey, String deliverId, String dedCode,
                                        String content, String checkCode, OrgInfoDto org, Map<String, String> kwargs) {
        if (StringUtils.isEmpty(content)) {
            content = getSurveyDedicatedDwzUrl(orgId, survey.getId(), deliverId, dedCode, checkCode, org, kwargs);
        }
        // 生成二维码
        String fileName = CommonUtil.fileNameFormat(dedCode);
        String tmpPath = ROOT_FILE_PATH + File.separator + StrUtil.replace(OssPathEnum.TEMP_DELIVER.getPath(), "/", File.separator);
        String ossPath = OssPathEnum.QRCODE.getPath() + survey.getId() + SEPARATOR + deliverId;
        saveMatrixImgByParam(content, tmpPath, ossPath, fileName, false);
    }

    @Override
    public void afterPropertiesSet() {
        getDeliverMemberIdListFunctionMapInit();
        getBaseAndMemberIdListByDeliverIdFunctionMapInit();
    }

    private void getDeliverMemberIdListFunctionMapInit() {
        Function<String, List<String>> sms = id -> smsDeliverMapper.getDeliverMemberIdList(id);
        Function<String, List<String>> email = id -> emailDeliverMapper.getDeliverMemberIdList(id);
        Function<String, List<String>> custom = id -> customDeliverMapper.getDeliverMemberIdList(id);
        Function<String, List<String>> wechatmp = id -> wechatmpDeliverMapper.getDeliverMemberIdList(id);
        Function<String, List<String>> customApi = id -> {
            LambdaQueryWrapper<DeliverCustomApiDeliver> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(DeliverCustomApiDeliver::getDeliverId, id).last("limit 1");
            DeliverCustomApiDeliver one = customApiDeliverMapper.selectOne(queryWrapper);
            return JSONArray.parseArray(one.getMemberIdlist(), String.class);
        };

        getDeliverMemberIdListFunctionMap.put(DELIVER_TTYPE_SMS, sms);
        getDeliverMemberIdListFunctionMap.put(DELIVER_TTYPE_EMAIL, email);
        getDeliverMemberIdListFunctionMap.put(DELIVER_TTYPE_CUSTOM, custom);
        getDeliverMemberIdListFunctionMap.put(DELIVER_TTYPE_WECHATMP, wechatmp);
        getDeliverMemberIdListFunctionMap.put(DELIVER_TTYPE_CUSTOM_API, customApi);
    }

    private void getBaseAndMemberIdListByDeliverIdFunctionMapInit() {
        Function<String, Object> sms = id -> smsDeliverMapper.getBaseAndMemberIdListByDeliverId(id);
        Function<String, Object> email = id -> emailDeliverMapper.getBaseAndMemberIdListByDeliverId(id);
        Function<String, Object> custom = id -> customDeliverMapper.getBaseAndMemberIdListByDeliverId(id);
        Function<String, Object> wechatmp = id -> wechatmpDeliverMapper.getBaseAndMemberIdListByDeliverId(id);
        Function<String, Object> customApi = id -> customApiDeliverMapper.getBaseAndMemberIdListByDeliverId(id);

        getBaseAndMemberIdListByDeliverIdFunctionMap.put(DELIVER_TTYPE_SMS, sms);
        getBaseAndMemberIdListByDeliverIdFunctionMap.put(DELIVER_TTYPE_EMAIL, email);
        getBaseAndMemberIdListByDeliverIdFunctionMap.put(DELIVER_TTYPE_CUSTOM, custom);
        getBaseAndMemberIdListByDeliverIdFunctionMap.put(DELIVER_TTYPE_WECHATMP, wechatmp);
        getBaseAndMemberIdListByDeliverIdFunctionMap.put(DELIVER_TTYPE_CUSTOM_API, customApi);
    }

    @Override
    public List<String> getDeliverMemberIdList(String deliver_id, DeliverTtypeEnum deliveEnum) {
        if (getDeliverMemberIdListFunctionMap.containsKey(deliveEnum)) {
            return getDeliverMemberIdListFunctionMap.get(deliveEnum).apply(deliver_id);
        } else {
            return Collections.emptyList();
        }
    }

    @Override
    public Object getBaseAndMemberIdListByDeliverId(String deliver_id, DeliverTtypeEnum deliveEnum) {
        if (getBaseAndMemberIdListByDeliverIdFunctionMap.containsKey(deliveEnum)) {
            return getBaseAndMemberIdListByDeliverIdFunctionMap.get(deliveEnum).apply(deliver_id);
        } else {
            return new Object();
        }
    }


    @Override
    public JSONObject getDeliverById(String id, GetOneDeliverParam param) {
        if (StringUtils.isEmpty(param.getProjectId())) {
            throw new BusinessException("project_id required");
        }
        JSONObject resultJs = new JSONObject();
        if (StringUtils.isNotEmpty(id)) {
            DeliverDeliver deliver = baseMapper.selectById(id);
            if (deliver == null) {
                throw new BusinessException("该投放不存在");
            }
            Integer status = deliver.getStatus();
            Integer isViewDetail = param.getIsViewDetail();
            if (isViewDetail != null && isViewDetail == 0) {
                if (status == DELIVER_STATUS_FINISH.getNumber()) {
                    throw new BusinessException("该投放已结束");
                }
                if (status == DELIVER_STATUS_REVIEW_SUCCESS.getNumber()) {
                    throw new BusinessException("该投放已审核通过");
                }
                if (status == DELIVER_STATUS_REVIEW_FAIL.getNumber()) {
                    throw new BusinessException("该投放已审核驳回");
                }
            }
            Integer value = deliver.getTtype();
            if (value == DELIVER_TTYPE_SMS.getValue()) {
                LambdaQueryWrapper<DeliverSmsDeliver> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeliverSmsDeliver::getDeliverId, deliver.getId()).last("limit 1");
                DeliverSmsDeliver smsDeliver = smsDeliverMapper.selectOne(queryWrapper);
                if (smsDeliver != null) {
                    JSONObject smsDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(smsDeliver));
                    LambdaQueryWrapper<DeliverSmsDeliverMemberId> qWrapper = new LambdaQueryWrapper<>();
                    qWrapper.eq(DeliverSmsDeliverMemberId::getSmsdeliverId, smsDeliver.getId());
                    long deliverMemberCount = smsDeliverMemberIdMapper.selectCount(qWrapper);

                    smsDeliverJs.put("sms_template", StrUtil.isNotEmpty(smsDeliver.getSmsTemplateId()) ? smsTemplateMapper.selectById(smsDeliver.getSmsTemplateId()) : null);

                    LambdaQueryWrapper<DeliverSmsRecord> rWrapper = new LambdaQueryWrapper<>();
                    rWrapper.eq(DeliverSmsRecord::getSmsDeliverId, smsDeliver.getId());
                    List<Integer> statusList = new ArrayList<>();
                    statusList.add(SMSRECORD_STATUS_SENDING.getNumber());
                    statusList.add(SMSRECORD_STATUS_SUCCESS.getNumber());
                    rWrapper.in(DeliverSmsRecord::getStatus, statusList);
                    long count = smsRecordMapper.selectCount(rWrapper);
                    smsDeliverJs.put("count", count);

                    JSONObject innerDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(deliver));
                    if (StringUtils.isNotEmpty(deliver.getAwardId())) {
                        AwardDto award = getAward(deliver.getAwardId(), false);
                        innerDeliverJs.put("award", award);
                    }
                    long deliverCount;
                    smsDeliverJs.put("deliver", innerDeliverJs);
                    if (smsDeliverJs.get("filter_params") != null) {
                        if (smsDeliverJs.getJSONObject("filter_params").get("is_mproject") != null) {
                            deliverCount = StringUtils.isEmpty(smsDeliver.getMprojectMobile()) ? 0
                                    : JSONArray.parseArray(smsDeliver.getMprojectMobile(), String.class).size();
                        } else {
                            deliverCount = deliverMemberCount;
                        }
                    } else {
                        deliverCount = deliverMemberCount;
                    }
                    smsDeliverJs.put("deliver_count", deliverCount);
                    resultJs = smsDeliverJs;
                }
            } else if (value == DELIVER_TTYPE_EMAIL.getValue()) {
                LambdaQueryWrapper<DeliverEmailDeliver> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeliverEmailDeliver::getDeliverId, deliver.getId()).last("limit 1");
                DeliverEmailDeliver emailDeliver = emailDeliverMapper.selectOne(queryWrapper);
                if (emailDeliver != null) {
                    JSONObject emailDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(emailDeliver));
                    LambdaQueryWrapper<DeliverEmailDeliverMemberId> mWrapper = new LambdaQueryWrapper<>();
                    mWrapper.eq(DeliverEmailDeliverMemberId::getEmaildeliverId, emailDeliver.getId());
                    long deliverCount = emailDeliverMemberIdMapper.selectCount(mWrapper);

                    LambdaQueryWrapper<DeliverEmailRecord> rWrapper = new LambdaQueryWrapper<>();
                    rWrapper.eq(DeliverEmailRecord::getMsgid, emailDeliver.getId());
                    long count = emailRecordMapper.selectCount(rWrapper);
                    emailDeliverJs.put("count", count);

                    JSONObject innerDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(deliver));
                    if (StringUtils.isNotEmpty(deliver.getAwardId())) {
                        AwardDto award = getAward(deliver.getAwardId(), false);
                        innerDeliverJs.put("award", award);
                    }
                    emailDeliverJs.put("deliver", innerDeliverJs);
                    emailDeliverJs.put("deliver_count", deliverCount);
                    resultJs = emailDeliverJs;
                }
            } else if (value == DELIVER_TTYPE_CUSTOM.getValue()) {
                LambdaQueryWrapper<DeliverCustomDeliver> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeliverCustomDeliver::getDeliverId, deliver.getId()).last("limit 1");
                DeliverCustomDeliver customDeliver = customDeliverMapper.selectOne(queryWrapper);
                if (customDeliver != null) {
                    JSONObject customDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(customDeliver));
                    LambdaQueryWrapper<DeliverCustomDeliverMemberId> mWrapper = new LambdaQueryWrapper<>();
                    mWrapper.eq(DeliverCustomDeliverMemberId::getCustomDeliverId, customDeliver.getId());
                    long deliverCount = customDeliverMemberIdMapper.selectCount(mWrapper);

                    LambdaQueryWrapper<DeliverCustomDeliverNodisturbMemberId> noWrapper = new LambdaQueryWrapper<>();
                    noWrapper.eq(DeliverCustomDeliverNodisturbMemberId::getCustomDeliverId, customDeliver.getId());
                    List<DeliverCustomDeliverNodisturbMemberId> noDisturbMembers = customDeliverNodisturbMemberIdMapper.selectList(noWrapper);
                    List<String> noMemberList = noDisturbMembers.stream()
                            .map(DeliverCustomDeliverNodisturbMemberId::getMemberId).collect(Collectors.toList());
                    customDeliverJs.put("no_disturb_member_id_list", noMemberList);

                    JSONObject innerDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(deliver));
                    if (StringUtils.isNotEmpty(deliver.getAwardId())) {
                        AwardDto award = getAward(deliver.getAwardId(), false);
                        innerDeliverJs.put("award", award);
                    }
                    customDeliverJs.put("deliver", innerDeliverJs);
                    customDeliverJs.put("deliver_count", deliverCount);
                    resultJs = customDeliverJs;
                }
            } else if (value == DELIVER_TTYPE_CUSTOM_API.getValue()) {
                LambdaQueryWrapper<DeliverCustomApiDeliver> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeliverCustomApiDeliver::getDeliverId, deliver.getId()).last("limit 1");
                DeliverCustomApiDeliver customDeliver = customApiDeliverMapper.selectOne(queryWrapper);
                if (customDeliver != null) {
                    JSONObject customDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(customDeliver));

                    LambdaQueryWrapper<DeliverCustomApiRecord> rWrapper = new LambdaQueryWrapper<>();
                    rWrapper.eq(DeliverCustomApiRecord::getApiDeliverId, customDeliver.getId());
                    List<Integer> statusList = new ArrayList<>();
                    statusList.add(CUSTOMAPIRECORD_STATUS_SENDING.getValue());
                    statusList.add(CUSTOMAPIRECORD_STATUS_SUCCESS.getValue());
                    rWrapper.in(DeliverCustomApiRecord::getStatus, statusList);
                    long count = customApiRecordMapper.selectCount(rWrapper);
                    customDeliverJs.put("count", count);

                    JSONObject innerDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(deliver));
                    if (StringUtils.isNotEmpty(deliver.getAwardId())) {
                        AwardDto award = getAward(deliver.getAwardId(), false);
                        innerDeliverJs.put("award", award);
                    }
                    customDeliverJs.put("deliver", innerDeliverJs);
                    int deliverCount = StringUtils.isEmpty(customDeliver.getMemberIdlist()) ? 0
                            : JSONArray.parseArray(customDeliver.getMemberIdlist(), String.class).size();
                    customDeliverJs.put("deliver_count", deliverCount);
                    resultJs = customDeliverJs;
                }
            } else if (value == DELIVER_TTYPE_SAMPLE.getValue()) {
                JSONObject innerDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(deliver));
                if (StringUtils.isNotEmpty(deliver.getAwardId())) {
                    AwardDto award = getAward(deliver.getAwardId(), false);
                    innerDeliverJs.put("award", award);
                }
                resultJs = innerDeliverJs;
            } else if (value == DELIVER_TTYPE_WECHATMP.getValue()) {
                LambdaQueryWrapper<DeliverWechatmpDeliver> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeliverWechatmpDeliver::getDeliverId, deliver.getId()).last("limit 1");
                DeliverWechatmpDeliver wechatDeliver = wechatmpDeliverMapper.selectOne(queryWrapper);
                if (wechatDeliver != null) {
                    JSONObject wechatDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(wechatDeliver));
                    LambdaQueryWrapper<DeliverWechatmpDeliverMemberId> mWrapper = new LambdaQueryWrapper<>();
                    mWrapper.eq(DeliverWechatmpDeliverMemberId::getWechatmpdeliverId, wechatDeliver.getId());
                    long deliverCount = wechatmpDeliverMemberIdMapper.selectCount(mWrapper);

                    LambdaQueryWrapper<DeliverWechatmpRecord> noWrapper = new LambdaQueryWrapper<>();
                    noWrapper.eq(DeliverWechatmpRecord::getMpDeliverId, wechatDeliver.getId());
                    long count = wechatmpRecordMapper.selectCount(noWrapper);
                    wechatDeliverJs.put("count", count);

                    JSONObject innerDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(deliver));
                    if (StringUtils.isNotEmpty(deliver.getAwardId())) {
                        AwardDto award = getAward(deliver.getAwardId(), false);
                        innerDeliverJs.put("award", award);
                    }
                    wechatDeliverJs.put("deliver", innerDeliverJs);
                    wechatDeliverJs.put("deliver_count", deliverCount);
                    resultJs = wechatDeliverJs;
                }
            } else if (value == DELIVER_TTYPE_WECHAT.getValue()) {
                LambdaQueryWrapper<DeliverWechatDeliver> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeliverWechatDeliver::getDeliverId, deliver.getId()).last("limit 1");
                DeliverWechatDeliver wechatDeliver = wechatDeliverMapper.selectOne(queryWrapper);
                if (wechatDeliver != null) {
                    JSONObject wechatDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(wechatDeliver));
                    JSONObject innerDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(deliver));
                    if (StringUtils.isNotEmpty(deliver.getAwardId())) {
                        AwardDto award = getAward(deliver.getAwardId(), false);
                        innerDeliverJs.put("award", award);
                    }
                    wechatDeliverJs.put("deliver", innerDeliverJs);
                    resultJs = wechatDeliverJs;
                }
            } else if (value == DELIVER_TTYPE_CUSTOM_LINK.getValue()) {
                DeliverCustomLinkDeliver linkDeliver = linkDeliverMapper.selectOne(
                        new LambdaQueryWrapper<DeliverCustomLinkDeliver>()
                                .eq(DeliverCustomLinkDeliver::getDeliverId, deliver.getId()));
                DeliverExtDeliverMapping extDeliverMapping = extDeliverMappingMapper.selectOne(
                        new LambdaQueryWrapper<DeliverExtDeliverMapping>()
                                .eq(DeliverExtDeliverMapping::getDeliverId, deliver.getId()));
                if (linkDeliver != null) {
                    JSONObject linkDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(linkDeliver));
                    JSONObject extDeliverMappingJs = JSONObject.parseObject(JSONObject.toJSONString(extDeliverMapping));
                    JSONObject innerDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(deliver));
                    linkDeliverJs.put("deliver", innerDeliverJs);
                    linkDeliverJs.put("ext_deliver_mapping", extDeliverMappingJs);
                    resultJs = linkDeliverJs;
                }
            } else {
                JSONObject innerDeliverJs = JSONObject.parseObject(JSONObject.toJSONString(deliver));
                if (StringUtils.isNotEmpty(deliver.getAwardId())) {
                    AwardDto award = getAward(deliver.getAwardId(), false);
                    innerDeliverJs.put("award", award);
                }
                resultJs = innerDeliverJs;
            }

            JSONObject item = resultJs;
            DeliverSurveyDeliver surveyDeliver = deliverSurveyDeliverMapper.selectOne(new LambdaQueryWrapper<DeliverSurveyDeliver>().eq(DeliverSurveyDeliver::getDeliverId, id).last("limit 1"));
            Project project = null;
            if (ObjectUtil.isNotNull(surveyDeliver)) {
                // 获取项目添加项目对应的标题和项目状态
                project = qdesGrpcClient.getProject(surveyDeliver.getSurveyId());
            }

            if (project != null) {
                item.put("survey_title", project.getTitle());
                item.put("survey_status", project.getStatus().getNumber());
                if (deliver.getTtype() == DELIVER_TTYPE_DEFAULT_STORE.getNumber()) {
                    LambdaQueryWrapper<DeliverDefaultDeliverStore> defaultWrapper = Wrappers.lambdaQuery();
                    defaultWrapper.eq(DeliverDefaultDeliverStore::getDeliverId, id)
                            .eq(DeliverDefaultDeliverStore::getStatus, DEFAULTDELIVERSTORE_STATUS_ACTIVATE.getNumber());
                    List<DeliverDefaultDeliverStore> deliverStores = deliverStoreMapper.selectList(defaultWrapper);
                    List<String> groupIds = deliverStores.stream().map(DeliverDefaultDeliverStore::getGroupId).collect(Collectors
                            .toList());
                    item.put("finished", surveyGrpcClient.getDeliverRspdAmount(id, null, groupIds, null,
                            RESPONDENT_STATUS_FINISH.getValue(), null, param.getProjectId()));
                } else if (deliver.getTtype() == DELIVER_TTYPE_DEDICATED.getNumber()) {
                    LambdaQueryWrapper<DeliverDedicatedDeliver> defaultWrapper = Wrappers.lambdaQuery();
                    defaultWrapper.eq(DeliverDedicatedDeliver::getDeliverId, id)
                            .eq(DeliverDedicatedDeliver::getStatus, DEFAULTDELIVERSTORE_STATUS_ACTIVATE.getNumber());
                    List<DeliverDedicatedDeliver> deliverStores = dedicateDdeliverMapper.selectList(defaultWrapper);
                    List<String> codeList = deliverStores.stream().map(DeliverDedicatedDeliver::getCode).collect(Collectors
                            .toList());
                    item.put("finished", surveyGrpcClient.getDeliverRspdAmount(id, null, null, null,
                            RESPONDENT_STATUS_FINISH.getValue(), codeList, param.getProjectId()));
                } else {
                    item.put("finished", surveyGrpcClient.getDeliverRspdAmount(id, null, null, null,
                            RESPONDENT_STATUS_FINISH.getValue(), null, param.getProjectId()));
                }
                if (deliver.getTtype() == DELIVER_TTYPE_SMS.getValue()
                        || deliver.getTtype() == DELIVER_TTYPE_EMAIL.getValue()
                        || deliver.getTtype() == DELIVER_TTYPE_WECHATMP.getValue()) {
                    Integer checksubmit = getDeliverChecksubmit(deliver.getId(), deliver.getTtype());
                    item.put("planed", getDeliverPlanTotalCount(deliver.getId(), deliver.getTtype()));
                    if (checksubmit == 1) {
                        Integer started = surveyGrpcClient.getDeliverRspdAmount(id, null, null, null,
                                null, null, param.getProjectId());
                        item.put("nostarted", item.getInteger("planed") - started);
                    }
                    item.put("processing", surveyGrpcClient.getDeliverRspdAmount(id, null, null, null,
                            RESPONDENT_STATUS_BEGIN.getValue(), null, param.getProjectId()));

                    String deliverMemberImportKey = "deliver.member_import." + id;
                    Object uploadSuccessRate = redisService.get(deliverMemberImportKey);
                    if (uploadSuccessRate != null) {
                        item.put("upload_success_rate", uploadSuccessRate);
                    }
                }
            }
        }
        return resultJs;
    }


    @Override
    public long getDeliverPlanTotalCount(String deliverId, int ttype) {
        // 获取某投放的投放对象数量
        long data = 0;
        if (ttype == DELIVER_TTYPE_SMS.getValue()) {
            LambdaQueryWrapper<DeliverSmsDeliver> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(DeliverSmsDeliver::getDeliverId, deliverId).last("limit 1");
            DeliverSmsDeliver smsDeliver = smsDeliverMapper.selectOne(wrapper);
            if (smsDeliver != null) {
                if (smsDeliver.getIsUpload() != null && smsDeliver.getIsUpload() != 0
                        && (smsDeliver.getIsSyncMem() == null || smsDeliver.getIsSyncMem() == 0)) {
                    data = JSONArray.parseArray(smsDeliver.getMobileList(), String.class).size();
                } else {
                    LambdaQueryWrapper<DeliverSmsDeliverMemberId> eWrapper = Wrappers.lambdaQuery();
                    eWrapper.eq(DeliverSmsDeliverMemberId::getSmsdeliverId, smsDeliver.getId());
                    data = smsDeliverMemberIdMapper.selectCount(eWrapper);
                }
            }
        }

        if (ttype == DELIVER_TTYPE_EMAIL.getValue()) {
            LambdaQueryWrapper<DeliverEmailDeliver> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(DeliverEmailDeliver::getDeliverId, deliverId).last("limit 1");
            DeliverEmailDeliver emailDeliver = emailDeliverMapper.selectOne(wrapper);
            if (emailDeliver == null) {
                data = 0;
            } else {
                if (emailDeliver.getIsUpload() != null && emailDeliver.getIsUpload() != 0
                        && (emailDeliver.getIsSyncMem() == null || emailDeliver.getIsSyncMem() == 0)) {
                    data = JSONArray.parseArray(emailDeliver.getEmailList(), String.class).size();
                } else {
                    LambdaQueryWrapper<DeliverEmailDeliverMemberId> eWrapper = Wrappers.lambdaQuery();
                    eWrapper.eq(DeliverEmailDeliverMemberId::getEmaildeliverId, emailDeliver.getId());
                    data = emailDeliverMemberIdMapper.selectCount(eWrapper);
                }
            }
        }

        if (ttype == DELIVER_TTYPE_WECHATMP.getValue()) {
            LambdaQueryWrapper<DeliverWechatmpDeliver> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(DeliverWechatmpDeliver::getDeliverId, deliverId).last("limit 1");
            DeliverWechatmpDeliver wechatDeliver = wechatmpDeliverMapper.selectOne(wrapper);
            if (wechatDeliver != null) {
                LambdaQueryWrapper<DeliverWechatmpDeliverMemberId> eWrapper = Wrappers.lambdaQuery();
                eWrapper.eq(DeliverWechatmpDeliverMemberId::getWechatmpdeliverId, wechatDeliver.getId());
                data = wechatmpDeliverMemberIdMapper.selectCount(eWrapper);
            }
        }
        return data;
    }

    // 生成压缩包上传后返回url给前端
    private Integer getDeliverChecksubmit(String deliverId, Integer type) {
        Integer data;
        if (type == DELIVER_TTYPE_SMS.getValue()) {
            LambdaQueryWrapper<DeliverSmsDeliver> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(DeliverSmsDeliver::getDeliverId, deliverId).last("limit 1");
            DeliverSmsDeliver smsDeliver = smsDeliverMapper.selectOne(queryWrapper);
            data = smsDeliver == null ? null : smsDeliver.getCheckSubmit();
        } else if (type == DELIVER_TTYPE_EMAIL.getValue()) {
            LambdaQueryWrapper<DeliverEmailDeliver> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(DeliverEmailDeliver::getDeliverId, deliverId).last("limit 1");
            DeliverEmailDeliver emailDeliver = emailDeliverMapper.selectOne(queryWrapper);
            data = emailDeliver == null ? null : emailDeliver.getCheckSubmit();
        } else if (type == DELIVER_TTYPE_WECHATMP.getValue()) {
            LambdaQueryWrapper<DeliverWechatmpDeliver> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(DeliverWechatmpDeliver::getDeliverId, deliverId).last("limit 1");
            DeliverWechatmpDeliver wechatDeliver = wechatmpDeliverMapper.selectOne(queryWrapper);
            data = wechatDeliver == null ? null : wechatDeliver.getCheckSubmit();
        } else {
            data = null;
        }
        return data;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createDeliver(CreateDeliverParam param) {
        if (param.getNums() > 1000) {
            throw new BusinessException("专用链接数超出上限1000");
        }

        // 插入deliver
        DeliverDeliver deliver = new DeliverDeliver();
        deliver.setName(param.getDeliverName());
        deliver.setTtype(DELIVER_TTYPE_DEDICATED.getNumber());
        deliver.setChannel(DELIVER_CHANNEL_DEDICATED.getNumber());
        deliver.setScenes(DELIVER_SCENES_DEDICATED.getNumber());
        deliver.setStatus(DELIVER_STATUS_START.getNumber());
        deliver.setStarttime(LocalDateTime.now());
        deliver.setCode(String.valueOf(serviceSequenceService.getSequence("DELIVER", 1)));
        // 设置默认值
        deliver.setDisable(0);
        deliver.setDelayTime("[]");
        deliver.setDelayFailedTime("{}");
        deliver.setAwardUseType(DELIVER_AWARD_USE_TYPE_UNUSE.getNumber());
        deliver.setEnableRespondent(1);
        deliver.setOrgId(param.getOrgId());
        deliver.setCreatedId(param.getUserId());
        baseMapper.insert(deliver);
        // 设置投放答题使能
        setDeliverRespondentEnableRedis(deliver.getId(), true);

        DeliverSurveyDeliver surveyDeliver = new DeliverSurveyDeliver();
        surveyDeliver.setDeliverId(deliver.getId());
        surveyDeliver.setSurveyId(param.getSurveyId());
        surveyDeliver.setOrgId(param.getOrgId());
        surveyDeliver.setCreatedId(param.getUserId());
        deliverSurveyDeliverMapper.insert(surveyDeliver);
        //这个mq不改状态，可以不用发
        StatusDto dto = new StatusDto();
        dto.setProjectId(param.getSurveyId());
        deliverMessageSendService.projectUpdated(dto);

        Project project = qdesGrpcClient.getProject(param.getSurveyId());
        if (project == null) {
            throw new BusinessException("项目不存在");
        }

        addDedicateddeliver(param.getNums(), param, project, deliver);
        doDefaultDeliverNameSeq(param.getDeliverName(), param.getSurveyId(), DELIVER_TTYPE_DEDICATED.getNumber());
        return deliver.getId();
    }


    @Override
    public ResultPage<DedicatedDeliverVo> getDeliver(Page<DeliverDedicatedDeliver> page, GetDeliverParam param) {

        DeliverDeliver deliver = baseMapper.selectById(param.getDeliverId());
        if (deliver == null) {
            throw new BusinessException("投放不存在");
        }
        String deliverName = deliver.getName();
        // 获取survey
        Project project = qdesGrpcClient.getProject(param.getSurveyId());

        LambdaQueryWrapper<DeliverDedicatedDeliver> defaultWrapper = Wrappers.lambdaQuery();
        defaultWrapper.eq(DeliverDedicatedDeliver::getDeliverId, param.getDeliverId())
                .eq(DeliverDedicatedDeliver::getSurveyId, param.getSurveyId())
                .eq(DeliverDedicatedDeliver::getOrgId, param.getOrgId())
                .orderByDesc(DeliverDedicatedDeliver::getCreatedDt);
        Page<DeliverDedicatedDeliver> pageResult = dedicateDdeliverMapper.selectPage(page, defaultWrapper);
        List<DeliverDedicatedDeliver> results = pageResult.getRecords();

        List<DedicatedDeliverVo> deliverList = new ArrayList<>();
        for (DeliverDedicatedDeliver oneDeliver : results) {
            DedicatedDeliverVo dedicateDeliverVo = deliverConvertMapper.dedicate2Vo(oneDeliver);
            dedicateDeliverVo.setSurveyRespStatus(getDeliverRespStatus(dedicateDeliverVo.getDeliverId(), null,
                    dedicateDeliverVo.getCode(), null, param.getSurveyId()));
            dedicateDeliverVo.setDeliverName(deliverName);
            // 获取二维码存放路径
            String qrcodRelPath = getQrcodePath(param.getOrgId(), project, deliver.getId(), null);
            if (StringUtils.isEmpty(qrcodRelPath)) {
                throw new BusinessException("Download file name does not exist");
            }
            dedicateDeliverVo.setUrl(qrcodRelPath);
            deliverList.add(dedicateDeliverVo);
        }
        return PageUtil.getResultPage(page.getCurrent(), pageResult.getTotal(), deliverList);
    }


    @Override
    public Object appendDeliver(AppendDeliverParam param) {
        // 专用链接投放 追加 投放
        if (param.getNums() > 1000) {
            throw new BusinessException("专用链接数超出上限1000");
        }
        Project project = qdesGrpcClient.getProject(param.getSurveyId());
        if (project == null) {
            throw new BusinessException("项目不存在");
        }

        DeliverDeliver deliver = baseMapper.selectById(param.getDeliverId());
        OrgInfoDto org = userGrpcClient.getOrganization(param.getOrgId());
        if (org == null) {
            throw new BusinessException(String.format("user.Organization[id=%s] not found", param.getOrgId()));
        }
        for (int i = 0; i < param.getNums(); i++) {
            createDedicateddeliverList(param.getOrgId(), project, deliver, org);
        }
        return param.getDeliverId();
    }


    @Override
    public String getDeliverDownloadPath(String surveyId, String deliverId, DeliverDownloadParam param) {
        Project project = qdesGrpcClient.getProject(surveyId);
        if (project == null) {
            throw new BusinessException("项目不存在");
        }
        DeliverDeliver deliver = baseMapper.selectById(deliverId);
        if (deliver == null) {
            throw new BusinessException("投放不存在");
        }

        return tarSurveyDedicatedQrcodes(project, deliver, param);
    }


    private String tarSurveyDedicatedQrcodes(Project survey, DeliverDeliver deliver, DeliverDownloadParam param) {
        // 专用链接二维码
        String zipName = "";
        List<DeliverDedicatedDeliver> dedicateDdelivers = new ArrayList<>();
        if (param.getFull()) {
            zipName = CommonUtil.fileNameFormat(survey.getTitle() + "_" + deliver.getName()) + "_"
                    + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH_mm_ss"));
            LambdaQueryWrapper<DeliverDedicatedDeliver> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(DeliverDedicatedDeliver::getDeliverId, deliver.getId())
                    .orderByDesc(DeliverDedicatedDeliver::getCreatedDt);
            dedicateDdelivers = dedicateDdeliverMapper.selectList(queryWrapper);
        } else {
            List<String> dedicatedDeliverIdList = param.getDedicatedDeliverIdList();
            if (CollectionUtils.isNotEmpty(dedicatedDeliverIdList)) {
                String hash = DigestUtil.md5Hex(dedicatedDeliverIdList.toString());
                zipName = CommonUtil.fileNameFormat(survey.getTitle() + "_" + deliver.getName()) + "_" + hash + "_"
                        + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH_mm_ss"));
                LambdaQueryWrapper<DeliverDedicatedDeliver> queryWrapper = Wrappers.lambdaQuery();
                queryWrapper.in(DeliverDedicatedDeliver::getId, dedicatedDeliverIdList)
                        .orderByDesc(DeliverDedicatedDeliver::getCreatedDt);
                dedicateDdelivers = dedicateDdeliverMapper.selectList(queryWrapper);
            }
        }
        // 替换文件名中的特殊字符为“_”
        zipName = CommonUtil.fileNameFormat(zipName);
        String qrcodPath = ROOT_FILE_PATH + File.separator + "qrcode";
        String tmpPath = qrcodPath + File.separator + zipName;
        File file1 = new File(tmpPath);
        if (!file1.exists()) {
            file1.mkdirs();
        }
        List<ExcelDeliverVo> excelDeliverVos = new ArrayList<>();
        String fileName = tmpPath + File.separator + "专用链接二维码投放.xlsx";
        for (DeliverDedicatedDeliver dDeliver : dedicateDdelivers) {
            ExcelDeliverVo excelVo = new ExcelDeliverVo();
            excelVo.setDeliverName(deliver.getName());
            excelVo.setLink(dDeliver.getLink());
            excelVo.setCreatedAt(dDeliver.getCreatedDt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            excelDeliverVos.add(excelVo);
        }
        ExcelExportUtil.createExcelStream(excelDeliverVos, ExcelDeliverVo.class, fileName);
        /*String filePath = ossService.getFilePrefix() + OssPathEnum.QRCODE.getPath() + survey.getId()
                + SEPARATOR + deliver.getId();*/
        String filePath = storageService.getPropertiesValueBy(null, "base-path") + OssPathEnum.QRCODE.getPath() + survey.getId()
                + SEPARATOR + deliver.getId();
        for (DeliverDedicatedDeliver dDeliver : dedicateDdelivers) {
            String deliverCode = dDeliver.getCode();
            fileName = CommonUtil.fileNameFormat(deliverCode + ".png");
            String path = filePath + SEPARATOR + fileName;
            //File fileDeliver = new File(tmpPath + SEPARATOR + fileName);
            //ossService.downloadFile(path, fileDeliver);
            //storageService.downloadFile(path, fileDeliver.getPath());
            //storageService.downloadByUrl(storageService.getPropertiesValueBy(null, "domain") + path).file(fileDeliver);

            if (!storageService.exists(storageService.getPropertiesValueBy(null, "domain") + path)) {
                //if (!fileDeliver.exists() || fileDeliver.length() == 0) {
                String content = getSurveyGroupDwzUrl(param.getOrgId(), survey.getId(), deliver.getId(), new HashMap<>());
                String srcUrl = createSurveyGroupQrcodes(param.getOrgId(), survey.getId(), deliver.getId(), content, "通用.png", true);
                File sourceFile = new File(srcUrl);
                try {
                    FileUtils.copyFile(sourceFile, new File(tmpPath + File.separator + fileName));
                } catch (IOException e) {
                    log.error("拷贝文件失败:", e);
                    throw new BusinessException("拷贝文件失败");
                }
                sourceFile.delete();
            }
        }
        ZipUtil.zipHutool(tmpPath, qrcodPath + File.separator + zipName + ".zip");
        File zipFile = new File(qrcodPath + File.separator + zipName + ".zip");
        /*String url = ossService.uploadSimpleFile2(zipFile, zipName + ".zip", OssPathEnum
                .QRCODE.getPath() + survey.getId() + SEPARATOR + deliver.getId() + SEPARATOR, 0, null, "", OssPathEnum.DELIVER);*/
        String url = storageService.upload(zipFile, zipName + ".zip", OssPathEnum
                .QRCODE.getPath() + survey.getId() + SEPARATOR + deliver.getId() + SEPARATOR, new Dict()).getUrl();
        zipFile.delete();
        FileUtil.del(file1);
        //if (file1.listFiles() != null) {
        //    for (File file : file1.listFiles()) {
        //        file.delete();
        //    }
        //}
        //file1.delete();
        return url;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer deliverDelete(String surveyId, String deliverId, DeliverDownloadParam param) {
        // DeliverDeliver deliver = baseMapper.selectById(deliverId);
        LambdaQueryWrapper<DeliverDedicatedDeliver> deleteWrapper = Wrappers.lambdaQuery();
        if (param.getFull()) {
            deleteWrapper.eq(DeliverDedicatedDeliver::getDeliverId, deliverId);
        } else {
            deleteWrapper.in(DeliverDedicatedDeliver::getId, param.getDedicatedDeliverIdList());
        }
        return dedicateDdeliverMapper.delete(deleteWrapper);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public XmResultJson<?> singleSurveyNoDisturb(JSONObject param) {
        String orgId = SecurityContextHolder.getOrgId();
        String userId = SecurityContextHolder.getUserId();
        //1、判断是否是审核者
        boolean checkPermission = userGrpcClient.checkCurrentChecker(userId, orgId);
        if (checkPermission) {
            List<String> stringList = userGrpcClient.listOrgAdminIds(orgId);
            if (CollectionUtils.isEmpty(stringList)) {
                log.error("[deliver] 组织 {} 下无用户", orgId);
                return XmResultJson.fail(-1, orgId + "组织下无用户");
            }
            userId = stringList.get(0);
        }

        //2.按照当前用户的权限查找项目
        List<ProjectEntry> projectEntryList = qdesGrpcClient.listProjectEntry(userId);
        List<String> objectList = projectEntryList.stream().map(ProjectEntry::getProjectId).distinct().collect(Collectors.toList());

        //判断uid是不是管理员或者是不是当前项目的创建者，只有管理员和当前项目创建者才能操作当前单项母的免打扰规则
        if (SecurityContextHolder.getToken().getIsSenior() != 1 || SecurityContextHolder.getToken().getSuperRole() != 1) {
            String surveyId = param.getJSONArray(NO_DISTURB_LIST).getJSONObject(0).getString(SURVEY_ID);
            Project project = qdesGrpcClient.getProject(surveyId);
            if (!project.getCreator().equals(userId)) {
                return XmResultJson.fail(-1, "没有权限操作");
            }
        }

        if (!param.getJSONArray(DELETE_IDS).isEmpty()) {
            JSONArray deleteIds = param.getJSONArray(DELETE_IDS);
            deliverDisturbRuleMapper.deleteBatchIds(deleteIds);
        }

        JSONArray noDisturbArray = param.getJSONArray(NO_DISTURB_LIST);
        for (int i = 0; i < noDisturbArray.size(); i++) {

            List<String> surverids = noDisturbArray.getJSONObject(i).getJSONArray("disturb_survey_ids").toJavaList(String.class);

            //获取未删除的projectid
            List<com.bestcem.xm.qdes.grpc.v1.services.Project> projectList = qdesGrpcClient.ListProjectsByIds(surverids);
            List<String> surveridTemp = projectList.stream()
                    .filter(x -> !(x.getStatus().getNumber() == PROJECT_STATUS_DELETED_VALUE || x.getStatus().getNumber() == PROJECT_STATUS_DELETED2_VALUE))
                    .map(com.bestcem.xm.qdes.grpc.v1.services.Project::getId)
                    .collect(Collectors.toList());

            //获取未删除且有权限操作的projectid
            surveridTemp.retainAll(objectList);

            DeliverDisturbRule deliverDisturbRule1 = JSON.toJavaObject(noDisturbArray.getJSONObject(i), DeliverDisturbRule.class);
            deliverDisturbRule1.setOrgId(orgId);
            deliverDisturbRule1.setDisturbSurveyIds(JSONArray.toJSONString(surveridTemp));
            if (noDisturbArray.getJSONObject(i).containsKey(ID)) {
                deliverDisturbRuleMapper.updateById(deliverDisturbRule1);
            } else {
                QueryWrapper<DeliverDisturbRule> queryWrapper = new QueryWrapper<>();
                queryWrapper.lambda().eq(DeliverDisturbRule::getOrgId, SecurityContextHolder.getOrgId());
                queryWrapper.lambda().eq(DeliverDisturbRule::getTtype, DISTURBRULE_TTYPE_SURVEY_VALUE);
                queryWrapper.lambda().eq(DeliverDisturbRule::getSurveyId, noDisturbArray.getJSONObject(i).getString("survey_id"));
                List<DeliverDisturbRule> cursor = deliverDisturbRuleMapper.selectList(queryWrapper);
                if (!org.springframework.util.CollectionUtils.isEmpty(cursor) && cursor.size() < 3) {
                    deliverDisturbRule1.setTtype(DISTURBRULE_TTYPE_SURVEY_VALUE);
                    deliverDisturbRuleMapper.insert(deliverDisturbRule1);
                }
            }

        }

        return XmResultJson.success();
    }


    @Override
    public XmResultJson<Map<String, List<Object>>> getSingleSurveyNoDisturb(String surveyId) {
        String orgId = SecurityContextHolder.getOrgId();
        DeliverDisturbRuleVo disturbRuleVo = new DeliverDisturbRuleVo();
        //创建查询条件对象
        LambdaQueryWrapper<DeliverDisturbRule> queryWrapper = Wrappers.lambdaQuery();
        //构造查询条件
        queryWrapper
                .eq(DeliverDisturbRule::getTtype, DISTURBRULE_TTYPE_SURVEY.getNumber())
                .eq(DeliverDisturbRule::getOrgId, orgId)
                .eq(DeliverDisturbRule::getSurveyId, surveyId)
                .last("limit 1");
        DeliverDisturbRule deliverDisturbRule = deliverDisturbRuleMapper.selectOne(queryWrapper);

        List<Object> objectList = new ArrayList<>();
        Map<String, List<Object>> mapList = new HashMap<>();
        // 判断该项目是否有单项目免打扰规则，有就返回，没有就创建
        if (ObjectUtil.isEmpty(deliverDisturbRule)) {
            deliverDisturbRule = new DeliverDisturbRule();
            deliverDisturbRule.setOrgId(orgId);
            deliverDisturbRule.setEnable(Boolean.FALSE);
            deliverDisturbRule.setTtype(DISTURBRULE_TTYPE_SURVEY.getNumber());
            deliverDisturbRule.setSurveyId(surveyId);
            deliverDisturbRule.setPeriodType(DISTURBRULE_PERIOD_TYPE_MONTH.getValue());
            deliverDisturbRule.setDays(0);
            deliverDisturbRule.setOpType(DISTURBRULE_OP_TYPE_DELIVER.getValue());
            deliverDisturbRule.setDisturbSurveyIds("[]");
            deliverDisturbRule.setLimitTimes(6);
            deliverDisturbRuleMapper.insert(deliverDisturbRule);

            BeanUtil.copyProperties(deliverDisturbRule, disturbRuleVo);
            disturbRuleVo.setProjecList(new ArrayList<>());
            objectList.add(disturbRuleVo);
        } else {
            queryWrapper.clear();
            queryWrapper
                    .eq(DeliverDisturbRule::getTtype, DISTURBRULE_TTYPE_SURVEY.getNumber())
                    .eq(DeliverDisturbRule::getOrgId, orgId)
                    .eq(DeliverDisturbRule::getSurveyId, surveyId);
            List<DeliverDisturbRule> cursor = deliverDisturbRuleMapper.selectList(queryWrapper);

            List<Object> no_disturbrule_list = getNoDisturbProjects(cursor);
            objectList.addAll(no_disturbrule_list);
        }
        mapList.put("no_disturbrule_list", objectList);
        return XmResultJson.success(mapList);
    }

    @Override
    public String deliverLinkDownload(GetDownloadLinkParam param) {
        Project project = qdesGrpcClient.getProject(param.getSurveyId());
        if (project == null) {
            throw new BusinessException("项目不存在");
        }
        DeliverDeliver deliver = baseMapper.selectById(param.getDeliverId());
        if (deliver == null) {
            throw new BusinessException("投放不存在");
        }
        Group group;
        if (StringUtils.isNotEmpty(param.getGroupId())) {
            group = userGrpcClient.getGroup(param.getGroupId());
        } else {
            group = null;
        }
        return tarSurveyGroupQrcodes(param.getOrgId(), project, deliver, group);
    }

    // 根据问卷编号及层级树打包所有二维码
    //    打包单个问卷二维码及其链接
    private String tarSurveyGroupQrcodes(String orgId, Project survey, DeliverDeliver deliver, Group group) {
        String zipName;
        String groupCode = group == null ? "" : group.getCode();
        String groupTitle = group == null ? "" : group.getName();
        zipName = survey.getTitle() + "_" + deliver.getName() + "_" + groupCode + "_"
                + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH_mm_ss"));
        // 替换文件名中的特殊字符为“_”
        zipName = CommonUtil.fileNameFormat(zipName);

        LambdaQueryWrapper<DeliverDefaultDeliverStore> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(DeliverDefaultDeliverStore::getSurveyId, survey.getId());
        if (group != null) {
            queryWrapper.eq(DeliverDefaultDeliverStore::getGroupId, group.getId());
        }
        queryWrapper.last("limit 1");
        DeliverDefaultDeliverStore defaultDeliverStore = deliverStoreMapper.selectOne(queryWrapper);
        if (defaultDeliverStore == null) {
            throw new BusinessException("默认投放不存在");
        }

        String qrcodPath = ROOT_FILE_PATH + File.separator + "qrcode";
        String tmpPath = qrcodPath + File.separator + zipName;
        File file1 = new File(tmpPath);
        if (!file1.exists()) {
            file1.mkdirs();
        }
        List<ExcelDeliverLinkVo> excelDeliverDTOS = new ArrayList<>();
        String fileName = tmpPath + File.separator + "组织链接.xlsx";
        ExcelDeliverLinkVo deliverDTO = new ExcelDeliverLinkVo();
        deliverDTO.setTitle(defaultDeliverStore.getTitle());
        deliverDTO.setLink(defaultDeliverStore.getLink());
        deliverDTO.setCreatedAt(defaultDeliverStore.getCreatedDt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        excelDeliverDTOS.add(deliverDTO);

        ExcelExportUtil.createExcelStream(excelDeliverDTOS, ExcelDeliverLinkVo.class, fileName);

        /*String filePath = ossService.getFilePrefix() + OssPathEnum.QRCODE.getPath() + survey.getId()
                + SEPARATOR + deliver.getId();*/
        String filePath = storageService.getPropertiesValueBy(null, "base-path") + OssPathEnum.QRCODE.getPath() + survey.getId()
                + SEPARATOR + deliver.getId();

        fileName = CommonUtil.fileNameFormat(groupCode + "@" + groupTitle + ".png");
        String path = filePath + SEPARATOR + fileName;

        //if (!fileDeliver.exists() || fileDeliver.length() == 0) {
        if (!storageService.exists(storageService.getPropertiesValueBy(null, "domain") + path)) {
            Map<String, String> keyMap = new HashMap<>();
            keyMap.put("code", groupCode);
            String content = getSurveyGroupDwzUrl(orgId, survey.getId(), deliver.getId(), keyMap);
            String srcUrl = createSurveyGroupQrcodes(orgId, survey.getId(), deliver.getId(), content, fileName, true);
            File sourceFile = new File(srcUrl);
            try {
                FileUtils.copyFile(sourceFile, new File(tmpPath + File.separator + fileName));
            } catch (IOException e) {
                log.error("拷贝文件失败", e);
                throw new BusinessException("拷贝文件失败");
            }
            sourceFile.delete();
        }

        ZipUtil.zipHutool(tmpPath, qrcodPath + File.separator + zipName + ".zip");
        File zipFile = new File(qrcodPath + File.separator + zipName + ".zip");
        /*String url = ossService.uploadSimpleFile2(zipFile, zipName + ".zip", OssPathEnum
                .QRCODE.getPath() + survey.getId() + SEPARATOR + deliver.getId() + SEPARATOR, 0, null, "", OssPathEnum.DELIVER);*/
        String url = storageService.upload(zipFile, zipName + ".zip", OssPathEnum
                .QRCODE.getPath() + survey.getId() + SEPARATOR + deliver.getId() + SEPARATOR, new Dict()).getUrl();
        zipFile.delete();
        FileUtil.del(file1);
        //if (file1.listFiles() != null) {
        //    for (File fileFile : file1.listFiles()) {
        //        fileFile.delete();
        //    }
        //}
        //file1.delete();
        return url;
    }


    @Override
    public String getDeliverTarPath(String surveyId, String deliverId, DeliverDefaultDownloadParam param, String rootGroupId,
                                    String search) {
        Project project = qdesGrpcClient.getProject(surveyId);
        if (project == null) {
            throw new BusinessException("项目不存在");
        }
        DeliverDeliver deliver = baseMapper.selectById(deliverId);
        if (deliver == null) {
            throw new BusinessException("投放不存在");
        }
        boolean includeInactive = false;
        List<String> specGroups = new ArrayList<>();
        // 层级投放下载包含失效门店
        if (deliver.getTtype() == DELIVER_TTYPE_REGION.getValue()) {
            includeInactive = true;
            LambdaQueryWrapper<DeliverSurveyRegion> queryWrapper = Wrappers.lambdaQuery();
            queryWrapper.eq(DeliverSurveyRegion::getDeliverId, deliverId)
                    .eq(DeliverSurveyRegion::getTtype, SURVEYREGION_TTYPE_STORE.getNumber());
            List<DeliverSurveyRegion> surveyRegions = surveyRegionMapper.selectList(queryWrapper);
            specGroups = surveyRegions == null ? new ArrayList<>() : surveyRegions.stream()
                    .map(DeliverSurveyRegion::getGroupId).collect(Collectors.toList());
        }
        GroupNode groupTree = null;
        JSONObject storeLevel = new JSONObject();
        if (StringUtils.isNotEmpty(rootGroupId)) {
            groupTree = userGrpcClient.getGroupTreeByRootGroupId(rootGroupId, includeInactive);
            List<JSONObject> levels = getGroupLevel(rootGroupId);
            if (CollectionUtils.isNotEmpty(levels)) {
                storeLevel = levels.get(levels.size() - 1);
            }
        }
        return tarSurveyGroupTreeQrcodes(param.getOrgId(), project, deliver, groupTree,
                storeLevel, search, specGroups, param);
    }


    // 获取某投放下某投放对象的问卷状态
    private Integer getDeliverRespStatus(String deliverId, String memberId, String dedCode, Integer status, String projectId) {
        return getDeliverRspdStatus(deliverId, memberId, dedCode, status, projectId);
    }

    private Integer getDeliverRspdStatus(String deliverId, String memberId, String dedCode, Integer status, String projectId) {
        // 获取某投放的投放对象的答题状态
        return surveyGrpcClient.getDeliverRspdStatus(deliverId, memberId, dedCode, status, projectId);
    }


    @Override
    public String getQrcodePath(String orgId, Project survey, String deliverId, Group group) {
        //String filePath = ossService.getFilePrefix() + OssPathEnum.QRCODE.getPath() + survey.getId() + SEPARATOR + deliverId;
        String filePath = storageService.getPropertiesValueBy(null, "base-path") + OssPathEnum.QRCODE.getPath() + survey.getId()
                + SEPARATOR + deliverId;
        String path = filePath + "/通用.png";
        if (group == null) {
            // 判断是否有该文件，没有的话就添加
            if (!storageService.exists(storageService.getPropertiesValueBy(null, "domain") + path)) {
                String content = getSurveyGroupDwzUrl(orgId, survey.getId(), deliverId, new HashMap<>());
                return createSurveyGroupQrcodes(orgId, survey.getId(), deliverId, content, "通用.png", false);
            }
        } else {
            String fileName = CommonUtil.fileNameFormat(group.getCode() + "@" + group.getName()) + ".png";
            path = filePath + SEPARATOR + fileName;
            if (!storageService.exists(storageService.getPropertiesValueBy(null, "domain") + path)) {
                Map<String, String> keyMap = new HashMap<>();
                keyMap.put("code", group.getCode());
                String content = getSurveyGroupDwzUrl(orgId, survey.getId(), deliverId, keyMap);
                return createSurveyGroupQrcodes(orgId, survey.getId(), deliverId, content, fileName, false);
            }
        }
        //return ossService.getCdn() + path;
        return storageService.getPropertiesValueBy(null, "domain") + path;
    }

    @Override
    public List<String> getNodisturbMemberIdByDeliver(String deliverId) {
        log.error("免打扰入参，deliverId={}", deliverId);
        List<String> no_disturb_member_id_list = new ArrayList<>();
        LambdaQueryWrapper<DeliverSurveyDeliver> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DeliverSurveyDeliver::getDeliverId, deliverId).last("limit 1");
        //获取已经启用的免打扰规则
        DeliverSurveyDeliver survey_deliver = deliverSurveyDeliverMapper.selectOne(wrapper);
        List<DeliverDisturbRule> disturb_rule_list = deliverDisturbRuleMapper.getAllEnableDisturbRule(survey_deliver.getOrgId(), survey_deliver.getSurveyId());
        for (DeliverDisturbRule disturb_rule : disturb_rule_list) {
            List<String> disturb_survey_ids = JSONArray.parseArray(disturb_rule.getDisturbSurveyIds(), String.class);
            List<String> tmp_member_list = getDisturbMemberIds(disturb_rule.getPeriodType(), disturb_rule.getDays(), disturb_rule.getOpType(), disturb_survey_ids, disturb_rule.getLimitTimes());
            no_disturb_member_id_list.addAll(tmp_member_list);
        }
        log.error("免打扰联系人列表，no_disturb_member_id_list={}", JSONArray.toJSONString(no_disturb_member_id_list));
        return no_disturb_member_id_list;
    }

    private List<String> getDisturbMemberIds(Integer period_type, Integer days, Integer op_type, List<String> disturb_survey_ids, Integer limit_times) {
        List<String> member_ids;
        Map<String, Long> timeMap = getDisturbTimeRange(period_type, days);
        Long start_time_int = timeMap.get("start_time_int");
        Long end_time_int = timeMap.get("end_time_int");
        if (op_type.equals(DISTURBRULE_OP_TYPE_DELIVER.getValue())) {
            member_ids = getMemberIdListDeliverInRange(start_time_int, end_time_int, disturb_survey_ids, limit_times);
        } else {
            member_ids = getMemberIdListSurveyInRange(start_time_int, end_time_int, disturb_survey_ids, limit_times);
        }
        return member_ids;
    }

    private List<String> getMemberIdListDeliverInRange(Long start_time, Long end_time, List<String> project_ids, Integer limit_times) {
        if (org.springframework.util.CollectionUtils.isEmpty(project_ids)) {
            throw new BusinessException("getMemberIdListDeliverInRange project_ids 必不能为空");
        }
        List<String> result = new ArrayList<>();
        LambdaQueryWrapper<DeliverSurveyDeliver> wrapper = Wrappers.lambdaQuery();
        wrapper.in(DeliverSurveyDeliver::getSurveyId, project_ids);
        List<DeliverSurveyDeliver> surveyDelivers = deliverSurveyDeliverMapper.selectList(wrapper);
        List<String> deliverIds = Objects.isNull(surveyDelivers) ? new ArrayList<>() : surveyDelivers.stream()
                .map(DeliverSurveyDeliver::getDeliverId).collect(Collectors.toList());

        if (CollectionUtils.isNotEmpty(deliverIds)) {
            LambdaQueryWrapper<DeliverDeliver> defaultWrapper = Wrappers.lambdaQuery();
            defaultWrapper.in(DeliverDeliver::getId, deliverIds);
            List<DeliverDeliver> deliverDelivers = baseMapper.selectList(defaultWrapper);
            for (DeliverDeliver deliver : deliverDelivers) {
                //如果是二维码投放，不需要免打扰
                if (deliver.getTtype().equals(DELIVER_TTYPE_REGION.getValue()) || deliver.getTtype().equals(DELIVER_TTYPE_DEFAULT.getValue())) {
                    continue;
                }

                List<String> mids = getMemberIdFromRecord(start_time, end_time, deliver);
                if (!CollectionUtils.isEmpty(mids)) {
                    result.addAll(mids);
                }
            }
        }

        //如果有次数限制并且次数限制大于1
        if (limit_times != null && limit_times > 1) {
            List<String> finalresult = new ArrayList<>();
            // 分组计数
            Map<String, Long> countMap = result.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
            countMap.forEach((k, v) -> {
                if (v > limit_times - 1) {
                    finalresult.add(k);
                }
            });
            return finalresult;
        } else {
            //没有次数限制，或者次数小于等于1
            return result.stream().distinct().collect(Collectors.toList());
        }
    }

    private List<String> getMemberIdListSurveyInRange(Long start_time, Long end_time, List<String> project_ids, Integer limit_times) {
        if (org.springframework.util.CollectionUtils.isEmpty(project_ids)) {
            throw new BusinessException("getMemberIdListSurveyInRange project_ids 必不能为空");
        }
        List<String> result = new ArrayList<>();
        for (String project_id : project_ids) {
            List<String> mids = surveyGrpcClient.getFinishedAllMemberIds(project_id, start_time, end_time);
            if (!CollectionUtils.isEmpty(mids)) {
                result.addAll(mids);
            }
        }

        //如果有次数限制并且次数限制大于1
        if (limit_times != null && limit_times > 1) {
            List<String> finalresult = new ArrayList<>();
            // 分组计数
            Map<String, Long> countMap = result.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
            countMap.forEach((k, v) -> {
                if (v > limit_times - 1) {
                    finalresult.add(k);
                }
            });
            return finalresult;
        } else {
            //没有次数限制，或者次数小于等于1
            return result.stream().distinct().collect(Collectors.toList());
        }
    }

    /**
     * 支持多种，没法使用LambdaQuery
     * 只写了微信公众号投放，其余的谁用谁加
     *
     * @param start_time
     * @param end_time
     * @param deliver
     * @return java.util.List<java.lang.String>
     * @author chenglong.yue <chenglong.yue@idiaoyan.com>
     * @date 2022/11/25 14:50
     */
    private List<String> getMemberIdFromRecord(Long start_time, Long end_time, DeliverDeliver deliver) {

        if (deliver.getTtype().equals(DELIVER_TTYPE_WECHATMP.getValue())) {
            //微信公众号 投放
            QueryWrapper<DeliverWechatmpRecord> wrapper = new QueryWrapper<>();
            setQueryWrapper(start_time, end_time, deliver, wrapper);
            List<Object> idsObj = wechatmpRecordMapper.selectObjs(wrapper);
            return idsObj.stream().map(Object::toString).collect(Collectors.toList());
        } else if (deliver.getTtype().equals(DELIVER_TTYPE_EMAIL.getValue())) {
            QueryWrapper<DeliverEmailRecord> wrapper = new QueryWrapper<>();
            setQueryWrapper(start_time, end_time, deliver, wrapper);
            List<Object> idsObj = emailRecordMapper.selectObjs(wrapper);
            return idsObj.stream().map(Object::toString).collect(Collectors.toList());
        } else if (deliver.getTtype().equals(DELIVER_TTYPE_SMS.getValue())) {
            QueryWrapper<DeliverSmsRecord> wrapper = new QueryWrapper<>();
            setQueryWrapper(start_time, end_time, deliver, wrapper);
            List<Object> idsObj = smsRecordMapper.selectObjs(wrapper);
            return idsObj.stream().map(Object::toString).collect(Collectors.toList());
        } else {
            return Collections.emptyList();
        }
    }

    private void setQueryWrapper(Long start_time, Long end_time, DeliverDeliver deliver, QueryWrapper<?> wrapper) {
        wrapper.select("member_id");
        LocalDateTime start = LocalDateTimeUtil.of(start_time);
        LocalDateTime end = LocalDateTimeUtil.of(end_time);
        wrapper.ge("created_dt", LocalDateTimeUtil.format(start, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        wrapper.le("created_dt", LocalDateTimeUtil.format(end, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        wrapper.select("member_id");
        wrapper.eq("deliver_id", deliver.getId());

        wrapper.isNotNull("member_id");
        wrapper.ne("member_id", "''");
        wrapper.ne("member_id", "'null'");


        wrapper.in("status", Arrays.asList(0, 1));
    }

    private Map<String, Long> getDisturbTimeRange(Integer period_type, Integer days) {
        LocalDateTime current_time = LocalDateTime.now();
        LocalDateTime start_time;
        //获取年，月，日，时，分，秒
        int year = current_time.getYear();
        int month = current_time.getMonth().getValue();
        int day = current_time.getDayOfMonth();
        //自然年
        if (period_type.equals(DISTURBRULE_PERIOD_TYPE_YEAR.getValue())) {
            start_time = LocalDateTime.of(year, Month.of(1), 1, 0, 0, 0);
        } else if (period_type.equals(DISTURBRULE_PERIOD_TYPE_QUARTER.getValue())) {
            //自然季度
            if (month == 1 || month == 2 || month == 3) {
                start_time = LocalDateTime.of(year, Month.of(1), 1, 0, 0, 0);
            } else if (month == 4 || month == 5 || month == 6) {
                start_time = LocalDateTime.of(year, Month.of(4), 1, 0, 0, 0);
            } else if (month == 7 || month == 8 || month == 9) {
                start_time = LocalDateTime.of(year, Month.of(7), 1, 0, 0, 0);
            } else {
                start_time = LocalDateTime.of(year, Month.of(10), 1, 0, 0, 0);
            }
        } else if (period_type.equals(DISTURBRULE_PERIOD_TYPE_MONTH.getValue())) {
            //自然月
            start_time = LocalDateTime.of(year, Month.of(month), 1, 0, 0, 0);
        } else if (period_type.equals(DISTURBRULE_PERIOD_TYPE_DAY.getValue())) {
            start_time = LocalDateTime.of(year, Month.of(month), day, 0, 0, 0);
        } else {
            start_time = current_time.toLocalDate().minusDays(days - 1).atStartOfDay();
        }
        Map<String, Long> result = new HashMap<>();
        result.put("start_time_int", LocalDateTimeUtil.toEpochMilli(start_time));
        result.put("end_time_int", LocalDateTimeUtil.toEpochMilli(current_time));
        return result;
    }

    @Override
    public List<DeliverDisturbRule> getAllEnableDisturbRule(String orgId, String surveyId) {
        QueryWrapper<DeliverDisturbRule> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(DeliverDisturbRule::getOrgId, orgId);
        queryWrapper.lambda().eq(DeliverDisturbRule::getEnable, 1);
        queryWrapper.and(wq -> {
            wq.lambda().eq(DeliverDisturbRule::getTtype, DISTURBRULE_TTYPE_SURVEY.getNumber());
            wq.lambda().eq(DeliverDisturbRule::getSurveyId, surveyId);
            wq.or();
            wq.lambda().eq(DeliverDisturbRule::getTtype, DISTURBRULE_TTYPE_GLOBAL.getNumber());
            wq.apply("JSON_CONTAINS(disturb_survey_ids, JSON_ARRAY('" + surveyId + "'))");
        });
        return deliverDisturbRuleMapper.selectList(queryWrapper);
    }

    @Override
    public String genSurveyLink(Map<String, String> tempMap, String deliverId, Map<String, String> kwargs) {
        if (!kwargs.isEmpty()) {
            return tempMap.get("baseUrl") + "/api/deliver/c_l_deliver?" + asUrlParams(kwargs);
        } else {
            return tempMap.get("baseUrl") + "/api/deliver/c_l_deliver";
        }
    }

    @Override
    public String getSurveyGroupDwzUrl(String orgId, String surveyId, String deliverId, Map<String, String> kwargs) {
        OrgInfoDto org = userGrpcClient.getOrganization(orgId);
        if (!kwargs.containsKey("code")) {
            kwargs.put("code", "");
        }
        // 生成专用链接投放的链接
        kwargs.put("source", String.valueOf(RESPONDENT_SOURCE_QRCODE.getNumber()));
        kwargs.put("scenes", String.valueOf(DELIVER_SCENES_QRCODE.getNumber()));
        JSONObject surveyLink = getSurveyLink(orgId, surveyId, deliverId, org.getCode(), kwargs);
        String url = surveyLink.getString("url");
        String domain = surveyLink.getString("domain");
        return getSurveyDwzLink(url, domain);
    }

    @Override
    public String createSurveyGroupQrcodes(String orgId, String projectId, String deliverId, String content, String fileName,
                                           boolean keepFile) {
        //OrgInfoDto org = userGrpcClient.getOrganization(orgId);
        // 生成专用链接投放的链接
        String tmpPath = ROOT_FILE_PATH + File.separator + StrUtil.replace(OssPathEnum.TEMP_DELIVER.getPath(), "/", File.separator);
        String ossPath = OssPathEnum.QRCODE.getPath() + projectId + SEPARATOR + deliverId;
        return saveMatrixImgByParam(content, tmpPath, ossPath, fileName, keepFile);
    }

    // 默认名称序号设置
    @Override
    public void doDefaultDeliverNameSeq(String deliverName, String projectId, int ttype) {
        // 检查是否是默认投放名  邮箱投放(4)
        String defaultName = DeliverDefaultTypeEnum.getDescByValue(ttype);
        // 空格、中英文括号处理
        if (ReUtil.isMatch(defaultName + "\\s*\\((\\d+)\\)$", deliverName)) {
            // 当前名称中的seq
            Integer curSeq = Integer.valueOf(ReUtil.getGroup1(defaultName + "\\s*\\((\\d+)\\)$", deliverName));
            Integer redisSeq = getCurSeqWithTtype(projectId, ttype);

            if (redisSeq.equals(curSeq)) {
                // 获取当前项目的所有投放名称
                List<String> existsDeliverNames = new ArrayList<>();

                LambdaQueryWrapper<DeliverSurveyDeliver> wrapper = Wrappers.lambdaQuery();
                wrapper.eq(DeliverSurveyDeliver::getSurveyId, projectId);
                List<DeliverSurveyDeliver> surveyDelivers = deliverSurveyDeliverMapper.selectList(wrapper);
                List<String> deliverIds = surveyDelivers == null ? new ArrayList<>() : surveyDelivers.stream()
                        .map(DeliverSurveyDeliver::getDeliverId).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(deliverIds)) {
                    LambdaQueryWrapper<DeliverDeliver> defaultWrapper = Wrappers.lambdaQuery();
                    defaultWrapper.in(DeliverDeliver::getId, deliverIds);
                    List<DeliverDeliver> deliverDelivers = baseMapper.selectList(defaultWrapper);
                    deliverDelivers.forEach(deliver -> existsDeliverNames.add(deliver.getName()));
                }
                // 生成下一个序号
                String key = "deliver:default_name_" + projectId + "_" + ttype;
                int nextSeq = serviceSequenceService.getSequence(key, 1);
                String nextName = defaultName + "(" + nextSeq + ")";
                // 判断下一个默认投放名称是否存在于数据库中，直到不存在
                while (existsDeliverNames.contains(nextName)) {
                    log.warn("The names contains {}, go on.", nextName);
                    key = "deliver:default_name_" + projectId + "_" + ttype;
                    nextSeq = serviceSequenceService.getSequence(key, 1);
                    nextName = defaultName + "(" + nextSeq + ")";
                }
            }
        }
    }

    private Integer getCurSeqWithTtype(String projectId, int ttype) {
        // 获取当前redis中的投放序号
        String key = "deliver:default_name_" + projectId + "_" + ttype;
        Integer seq = serviceSequenceService.getCurrentSequence(key);
        if (seq != null) {
            return seq;
        }
        log.info("Now to generate seq");
        return serviceSequenceService.getSequence(key, 1);
    }


    @Override
    public void setDeliverRespondentEnableRedis(String deliverId, boolean enableRespondent) {
        // 设置是容许答题到redis中
        String key = "deliver:deliver_enable_respondent" + deliverId;
        // 创建投放时默认是容许答题
        // 设置redis 值为”1“ 容许答题 值为”0“ 不容许答题
        redisService.set(key, enableRespondent ? "1" : "0");
    }


    @Override
    public String getSurveyDedicatedDwzUrl(String orgId, String surveyId, String deliverId, String dedCode,
                                           String checkCode, OrgInfoDto org, Map<String, String> kwargs) {
        // 生成专用链接投放的链接
        kwargs.put("ded_code", dedCode);
        kwargs.put("checkCode", checkCode);
        kwargs.put("checkSubmit", "1");
        kwargs.put("source", String.valueOf(RESPONDENT_SOURCE_DEDICATED.getNumber()));
        kwargs.put("scenes", String.valueOf(DELIVER_SCENES_QRCODE.getNumber()));
        JSONObject surveyLink = getSurveyLink(orgId, surveyId, deliverId, org.getCode(), kwargs);
        return getSurveyDwzLink(surveyLink.getString("url"), surveyLink.getString("domain"));
    }

    @Override
    public String getSurveyDwzLink(String link, String customDomainUrl) {
        return aioConvertShortUrl(link, customDomainUrl);
    }

    @Override
    public String aioConvertShortUrl(String url, String customDomainUrl) {
        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("url", url);
        JSONObject response = wjlinkShortUrl(paramMap);
        if (response == null || response.getInteger("code") != 0) {
            throw new BusinessException("短链接服务获取出错");
        }
        log.info("Now the shorten response = {}", response.toJSONString());
        if (StringUtils.isEmpty(customDomainUrl)) {
            return response.getString("short_url");
        }
        return customDomainUrl + "/t/" + response.getString("tcode");
    }

    private JSONObject wjlinkShortUrl(Map<String, String> paramMap) {
        //RetrofitClient 构建x-www-form-urlencoded，一直有问题，换个原生写法
        try (HttpResponse response = HttpRequest.post(longToShortUrl)
                //.header(Header.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
                .body(JSON.toJSONString(paramMap))
                .execute()) {

            if (response.isOk()) {
                return JSONObject.parseObject(response.body());
            } else {
                return null;
            }
        }
    }

    @Override
    public JSONObject getSurveyLink(String orgId, String surveyId, String deliverId, String orgCode, Map<String, String> kwargs) {
        JSONObject jsonObject = getOrgCustomSetting(orgId);
        String domain = Optional.ofNullable(jsonObject.getString("domain")).orElse("");
        String url;
        if (StringUtils.isNotEmpty(domain)) {
            url = String.format(serverProtocol + "://" + domain + surveySystemUri, surveyId);
        } else {
            url = String.format(surveySystemUrl + surveySystemUri, surveyId);
        }
        log.info("getSurveyLink orgCode=" + orgCode);
        kwargs.put("org_code", orgCode);
        if (StringUtils.isNotEmpty(deliverId)) {
            kwargs.put("deliver_id", deliverId);
        }

        Map<String, Object> signMap = new HashMap<>(kwargs);
        /*Map<String, Object> signMap = new HashMap<>();
        kwargs.keySet().forEach(key -> {
            signMap.put(key, kwargs.get(key));
        });*/
        String signature = SignUtil.genSign(signMap, deliverSecret);
        kwargs.put("signature", signature);
        url += "?" + asUrlParams(kwargs);
        JSONObject reJs = new JSONObject();
        reJs.put("url", url);
        if (StringUtils.isEmpty(domain)) {
            reJs.put("domain", domain);
        } else {
            reJs.put("domain", serverProtocol + "://" + domain);
        }
        return reJs;
    }

    @Override
    public String saveMatrixImgByParam(String content, String tmpPath, String ossPath, String name, boolean keepFile) {
        File file1 = new File(tmpPath);
        if (!file1.exists()) {
            file1.mkdirs();
        }
        name = StrUtil.addSuffixIfNot(name, ".png");
        File fileInfo = new File(tmpPath, name);
        CommonUtil.getQrCodeImg(content, fileInfo);
        ossPath = StrUtil.addSuffixIfNot(ossPath, SEPARATOR);
        //String ossFilePath = ossService.uploadSimpleFile2(fileInfo, name, ossPath, 0, null, "", OssPathEnum.DELIVER);
        String ossFilePath = storageService.upload(fileInfo, name, ossPath, new Dict()).getUrl();
        if (keepFile) {
            return StrUtil.addSuffixIfNot(tmpPath, File.separator) + name;
        } else {
            fileInfo.delete();
        }
        return ossFilePath;
    }

    @Override
    public void itemTofront(SurveyDeliverPageVo deliverVo, Project project) {
        if (project != null) {
            deliverVo.setTitle(project.getTitle());
            deliverVo.setStatus(project.getStatusValue());
        }
        if (deliverVo.getDeliverDocs() != null && StringUtils.isNotEmpty(deliverVo.getDeliverDocs().getAwardId())) {
            AwardDto award = getAward(deliverVo.getDeliverDocs().getAwardId(), false);
            deliverVo.getDeliverDocs().setAward(award);
        }
        if (deliverVo.getDeliverDocs() != null && deliverVo.getDeliverDocs().getTtype() != null) {
            int value = deliverVo.getDeliverDocs().getTtype();
            if (value == DELIVER_TTYPE_SMS.getValue()) {
                LambdaQueryWrapper<DeliverSmsDeliver> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeliverSmsDeliver::getDeliverId, deliverVo.getDeliverId()).last("limit 1");
                DeliverSmsDeliver deliver = smsDeliverMapper.selectOne(queryWrapper);
                if (deliver != null) {
                    deliverVo.setIsDelay(deliver.getIsDelay());
                    deliverVo.setDeliverTime(deliver.getDeliverTime());
                }
            } else if (value == DELIVER_TTYPE_EMAIL.getValue()) {
                LambdaQueryWrapper<DeliverEmailDeliver> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeliverEmailDeliver::getDeliverId, deliverVo.getDeliverId()).last("limit 1");
                DeliverEmailDeliver deliver = emailDeliverMapper.selectOne(queryWrapper);
                if (deliver != null) {
                    deliverVo.setIsDelay(deliver.getIsDelay());
                    deliverVo.setDeliverTime(deliver.getDeliverTime());
                }
            } else if (value == DELIVER_TTYPE_CUSTOM.getValue()) {
                LambdaQueryWrapper<DeliverCustomDeliver> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeliverCustomDeliver::getDeliverId, deliverVo.getDeliverId()).last("limit 1");
                DeliverCustomDeliver deliver = customDeliverMapper.selectOne(queryWrapper);
                if (deliver != null) {
                    deliverVo.setIsDelay(deliver.getIsDelay());
                    deliverVo.setDeliverTime(deliver.getDeliverTime());
                }
            } else if (value == DELIVER_TTYPE_CUSTOM_API.getValue()) {
                LambdaQueryWrapper<DeliverCustomApiDeliver> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeliverCustomApiDeliver::getDeliverId, deliverVo.getDeliverId()).last("limit 1");
                DeliverCustomApiDeliver deliver = customApiDeliverMapper.selectOne(queryWrapper);
                if (deliver != null) {
                    deliverVo.setIsDelay(deliver.getIsDelay());
                    deliverVo.setDeliverTime(deliver.getDeliverTime());
                }
            } else if (value == DELIVER_TTYPE_SAMPLE.getValue()) {
                LambdaQueryWrapper<DeliverSampleDeliver> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeliverSampleDeliver::getDeliverId, deliverVo.getDeliverId()).last("limit 1");
                DeliverSampleDeliver deliver = sampleDeliverMapper.selectOne(queryWrapper);
                if (deliver != null) {
                    deliverVo.setLink(deliver.getLink());
                    deliverVo.setIsDelay(0);
                }
            } else if (value == DELIVER_TTYPE_WECHATMP.getValue()) {
                LambdaQueryWrapper<DeliverWechatmpDeliver> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeliverWechatmpDeliver::getDeliverId, deliverVo.getDeliverId()).last("limit 1");
                DeliverWechatmpDeliver deliver = wechatmpDeliverMapper.selectOne(queryWrapper);
                if (deliver != null) {
                    deliverVo.setIsDelay(deliver.getIsDelay());
                    deliverVo.setDeliverTime(deliver.getDeliverTime());
                }
            } else {
                deliverVo.setIsDelay(0);
            }

            if (deliverVo.getIsDelay() != 0) {
                LambdaQueryWrapper<DeliverDeliver> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(DeliverDeliver::getId, deliverVo.getDeliverId()).last("limit 1");
                DeliverDeliver deliver = baseMapper.selectOne(queryWrapper);
                // 是定时投放并且投放状态在 未投放和审核成功时，需要添加闹铃显示
                if (deliver != null && (deliver.getStatus() == DELIVER_STATUS_STOP.getNumber()
                        || deliver.getStatus() == DELIVER_STATUS_REVIEW_SUCCESS.getNumber())) {
                    deliverVo.setAlarmClock(1);
                    // 判断投放是否过期
                    LocalDateTime deliverTime = deliverVo.getDeliverTime();
                    if (deliverTime != null) {
                        long deliverMills = deliverTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                        LocalDateTime now = LocalDateTime.now();
                        long nowMills = now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                        if (nowMills > deliverMills) {
                            deliverVo.setDeliverExpired(1);
                        } else {
                            deliverVo.setDeliverExpired(0);
                        }
                    }
                }
            }
        }
    }

    // 根据id获取单个奖励
    private AwardDto getAward(String awardId, boolean needWind) {
        return awardGrpcClient.getAward(awardId, needWind);
    }

    /**
     * 借助guava包的工具类转义,按key排序方便调试,
     * 自定义投放签名是sig，其余投放是signature，操
     */
    private String asUrlParams(Map<String, String> source) {
        Map<String, String> tmp = Maps.newTreeMap();
        String signature = source.get("signature");
        String sig = source.get("sig");
        source.remove("signature");
        source.remove("sig");
        // java8 语法
        source.forEach((k, v) -> {
            if (k != null && v != null) {
                try {
                    tmp.put(k, URLEncoder.encode(v, "utf-8"));
                } catch (UnsupportedEncodingException e) {
                    throw new BusinessException("转义url错误");
                }
            }
        });
        String join = Joiner.on("&").useForNull("").withKeyValueSeparator("=").join(tmp);
        if (StrUtil.isNotBlank(signature)) {
            return join + "&signature=" + signature;
        }
        if (StrUtil.isNotBlank(sig)) {
            return join + "&sig=" + sig;
        }
        return join;
    }

    // 查询租户自定义域名等信息
    private JSONObject getOrgCustomSetting(String orgId) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("orgId", orgId);
        jsonObject.put("enable", true);
        List<OrgCustomSetting> customSettings = userGrpcClient.listOrgCustomSetting(jsonObject);
        JSONObject reJs = new JSONObject();
        if (CollectionUtils.isNotEmpty(customSettings)) {
            reJs.put("domain", customSettings.get(0).getDomain());
            reJs.put("favicon", customSettings.get(0).getFavicon());
        }
        return reJs;
    }


    // 获取门店投放的成功完成数据
    @Override
    public Integer getDeliverRspdAmount(String deliverId, String groupId, List<String> groupIdList, String memberId,
                                        Integer status, List<String> dedCodeList, String projectId) {
        return surveyGrpcClient.getDeliverRspdAmount(deliverId, groupId, groupIdList, memberId, status, dedCodeList, projectId);
    }

    //private DeliverDeliverGroup originGetDeliverGroup(String deliverGid) {
    //    DeliverDeliverGroup deliverDeliverGroup = deliverDeliverGroupMapper.selectById(deliverGid);
    //    if (Objects.isNull(deliverDeliverGroup)) {
    //        throw new BusinessException(String.valueOf(ResourceErrorCodeEnum.NOT_FOUND), "投放组不存在");
    //    }
    //    return deliverDeliverGroup;
    //}

    //private void originUpdateDeliverGroup(DeliverGroupVo deliverGroupVo, EmailDeliversParam param) {
    //    String orgId = SecurityContextHolder.getOrgId();
    //    //如果参数中有member_id_list就先把原先数据删除
    //    if (CollectionUtils.isNotEmpty(deliverGroupVo.getDeliverList())) {
    //        List<DeliverDeliverGroupDeliver> deliverList = deliverGroupVo.getDeliverList();
    //        if (CollectionUtils.isNotEmpty(deliverList)) {
    //            LambdaQueryWrapper<DeliverDeliverGroupDeliver> queryWrapper = new LambdaQueryWrapper<>();
    //            queryWrapper.eq(DeliverDeliverGroupDeliver::getDeliverGroupId, deliverGroupVo.getId());
    //            deliverDeliverGroupDeliverMapper.delete(queryWrapper);
    //            createDeliverGroupDeliver(deliverList, deliverGroupVo.getId());
    //        }
    //        updateDeliverGroup(orgId, param.getSurveyId(), deliverGroupVo.getId());
    //    }
    //}

    //private void createDeliverGroupDeliver(List<DeliverDeliverGroupDeliver> deliverList, String groupId) {
    //    deliverList.forEach(v -> {
    //        DeliverDeliverGroupDeliver deliver = new DeliverDeliverGroupDeliver();
    //        deliver.setId(new ObjectId().toHexString());
    //        deliver.setDeliverId(v.getDeliverId());
    //        deliver.setDeliverGroupId(groupId);
    //        deliverDeliverGroupDeliverMapper.insert(deliver);
    //    });
    //}

    //private void updateDeliverGroup(String orgId, String surveyId, String id) {
    //    DeliverDeliverGroup group = new DeliverDeliverGroup();
    //    group.setOrgId(orgId);
    //    group.setSurveyId(surveyId);
    //    group.setId(id);
    //    deliverDeliverGroupMapper.updateById(group);
    //}


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDeliver(String deliverId) {
        DeliverDeliver deliver = new DeliverDeliver();
        deliver.setId(deliverId);
        deliver.setStatus(DeliverStatusEnum.DELIVER_STATUS_STOP.getValue());
        baseMapper.updateById(deliver);
    }

    @Override
    public List<Object> getNoDisturbProjects(List<DeliverDisturbRule> cursor) {
        if (CollectionUtils.isEmpty(cursor)) {
            return new ArrayList<>();
        }
        List<Object> objectList = new ArrayList<>();
        for (DeliverDisturbRule disturbrule : cursor) {
            List<Map<String, String>> disturbSurveyIds = new ArrayList<>();
            DeliverDisturbRuleVo disturbRuleVo = new DeliverDisturbRuleVo();
            BeanUtil.copyProperties(disturbrule, disturbRuleVo);
            List<String> projectIdList = JSONArray.parseArray(disturbrule.getDisturbSurveyIds(), String.class).stream().distinct().collect(Collectors.toList());
            if (!ObjectUtils.isEmpty(projectIdList)) {
                List<com.bestcem.xm.qdes.grpc.v1.services.Project> projectList = qdesGrpcClient.ListProjectsByIds(projectIdList);
                for (com.bestcem.xm.qdes.grpc.v1.services.Project item : projectList) {
                    Map<String, String> map = new HashMap<>();
                    map.put("id", item.getId());
                    map.put("title", item.getTitle());
                    disturbSurveyIds.add(map);
                }

                // List<ProjectEntry> projectEntryList = qdesGrpcClient.listProjectEntryByIds(projectIdList);
                //List<ProjectEntry> projectEntrySet = projectEntryList.stream()
                //        .filter(distinctByKey(ProjectEntry::getProjectId))
                //        .collect(Collectors.toList());
                //for (ProjectEntry projectEntry : projectEntrySet) {
                //    Map<String, String> map = new HashMap<>();
                //    map.put("id", projectEntry.getProjectId());
                //    map.put("title", projectEntry.getTitle());
                //    disturbSurveyIds.add(map);
                //}
            }

            disturbRuleVo.setProjecList(disturbSurveyIds);
            objectList.add(disturbRuleVo);
        }
        return objectList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public JSONObject getGlobalSurveyNoDisturb(String surveyId, Boolean enable) {
        JSONObject ret = new JSONObject();
        List<Object> objectList = new ArrayList<>();
        LambdaQueryWrapper<DeliverDisturbConfig> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(DeliverDisturbConfig::getOrgId, SecurityContextHolder.getOrgId());
        wrapper.last("limit 1");
        DeliverDisturbConfig disturbconfig = disturbConfigMapper.selectOne(wrapper);
        if (ObjectUtils.isEmpty(disturbconfig)) {
            disturbconfig = new DeliverDisturbConfig();
            disturbconfig.setOrgId(SecurityContextHolder.getOrgId());
            disturbconfig.setAutoAddDisturb(Boolean.FALSE);
            disturbConfigMapper.insert(disturbconfig);
        }
        ret.put("disturbconfig", disturbconfig);

        LambdaQueryWrapper<DeliverDisturbRule> lambdaQuery = Wrappers.lambdaQuery();
        lambdaQuery
                .eq(DeliverDisturbRule::getTtype, DISTURBRULE_TTYPE_GLOBAL.getNumber())
                .eq(DeliverDisturbRule::getOrgId, SecurityContextHolder.getOrgId())
                .last("limit 1");
        DeliverDisturbRule disturb = deliverDisturbRuleMapper.selectOne(lambdaQuery);
        if (ObjectUtils.isEmpty(disturb)) {
            DeliverDisturbRule deliverDisturbRule = new DeliverDisturbRule();
            deliverDisturbRule.setOrgId(SecurityContextHolder.getOrgId());
            deliverDisturbRule.setEnable(Boolean.FALSE);
            deliverDisturbRule.setTtype(DISTURBRULE_TTYPE_GLOBAL.getNumber());
            //deliverDisturbRule.setSurveyId("");
            deliverDisturbRule.setPeriodType(DISTURBRULE_PERIOD_TYPE_MONTH.getValue());
            deliverDisturbRule.setDays(0);
            deliverDisturbRule.setOpType(DISTURBRULE_OP_TYPE_DELIVER.getValue());
            deliverDisturbRule.setDisturbSurveyIds("[]");
            deliverDisturbRule.setLimitTimes(6);
            deliverDisturbRuleMapper.insert(deliverDisturbRule);

            DeliverDisturbRuleVo disturbRuleVo = new DeliverDisturbRuleVo();
            BeanUtil.copyProperties(deliverDisturbRule, disturbRuleVo);
            disturbRuleVo.setProjecList(new ArrayList<>());
            objectList.add(disturbRuleVo);
        } else {
            QueryWrapper<DeliverDisturbRule> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(DeliverDisturbRule::getOrgId, SecurityContextHolder.getOrgId());
            queryWrapper.lambda().eq(DeliverDisturbRule::getTtype, DISTURBRULE_TTYPE_GLOBAL.getNumber());
            queryWrapper.lambda().eq(enable != null, DeliverDisturbRule::getEnable, enable);
            queryWrapper.apply(StrUtil.isNotEmpty(surveyId), "JSON_CONTAINS(disturb_survey_ids, JSON_ARRAY('" + surveyId + "'))");
            List<DeliverDisturbRule> cursor = deliverDisturbRuleMapper.selectList(queryWrapper);
            List<Object> no_disturbrule_list = getNoDisturbProjects(cursor);
            objectList.addAll(no_disturbrule_list);
        }
        ret.put("no_disturbrule_list", objectList);
        return ret;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void globalSurveyNoDisturb(JSONObject param) {
        String orgId = SecurityContextHolder.getOrgId();
        boolean autoAddDisturb = param.getBoolean(AUTO_ADD_DISTURB);
        LambdaUpdateWrapper<DeliverDisturbConfig> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(DeliverDisturbConfig::getOrgId, orgId);
        DeliverDisturbConfig deliverDisturbConfig = new DeliverDisturbConfig();
        deliverDisturbConfig.setAutoAddDisturb(autoAddDisturb);
        disturbConfigMapper.update(deliverDisturbConfig, updateWrapper);

        JSONArray deleteIds = param.getJSONArray(DELETE_IDS);
        if (!deleteIds.isEmpty()) {
            deliverDisturbRuleMapper.deleteBatchIds(deleteIds);
        }

        JSONArray noDisturbList = param.getJSONArray(NO_DISTURB_LIST);
        for (int i = 0; i < noDisturbList.size(); i++) {
            List<com.bestcem.xm.qdes.grpc.v1.services.Project> projectList = qdesGrpcClient.ListProjectsByIds(noDisturbList.getJSONObject(i).getJSONArray("disturb_survey_ids").toJavaList(String.class));
            JSONArray disturbSurveyIds = new JSONArray();
            projectList.forEach(a -> {
                if (!(a.getStatus().getNumber() == PROJECT_STATUS_DELETED.getNumber() || a.getStatus().getNumber() == PROJECT_STATUS_DELETED2.getNumber())) {
                    disturbSurveyIds.add(a.getId());
                }
            });
            DeliverDisturbRule deliverDisturbRule = new DeliverDisturbRule();
            deliverDisturbRule.setDisturbSurveyIds(String.valueOf(disturbSurveyIds));
            deliverDisturbRule.setPeriodType(noDisturbList.getJSONObject(i).getInteger("period_type"));
            deliverDisturbRule.setEnable(Optional.ofNullable(noDisturbList.getJSONObject(i).getBoolean("enable")).orElse(Boolean.FALSE));
            deliverDisturbRule.setOpType(noDisturbList.getJSONObject(i).getInteger("op_type"));
            deliverDisturbRule.setLimitTimes(noDisturbList.getJSONObject(i).getInteger("limit_times"));
            deliverDisturbRule.setDays(noDisturbList.getJSONObject(i).getInteger("days"));
            deliverDisturbRule.setSurveyId(noDisturbList.getJSONObject(i).getString("survey_id"));
            if (ObjectUtil.isNotEmpty(noDisturbList.getJSONObject(i).getString("_id"))) {
                LambdaUpdateWrapper<DeliverDisturbRule> disturbRuleLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
                disturbRuleLambdaUpdateWrapper.eq(DeliverDisturbRule::getId, noDisturbList.getJSONObject(i).getString("_id"));
                deliverDisturbRuleMapper.update(deliverDisturbRule, disturbRuleLambdaUpdateWrapper);
            } else {
                LambdaQueryWrapper<DeliverDisturbRule> disturbRuleLambdaQueryWrapper = new LambdaQueryWrapper<>();
                disturbRuleLambdaQueryWrapper.eq(DeliverDisturbRule::getOrgId, orgId)
                        .eq(DeliverDisturbRule::getTtype, DISTURBRULE_TTYPE_GLOBAL.getNumber());
                if (deliverDisturbRuleMapper.selectList(disturbRuleLambdaQueryWrapper).size() < 3) {
                    deliverDisturbRule.setOrgId(orgId);
                    deliverDisturbRule.setTtype(DISTURBRULE_TTYPE_GLOBAL.getNumber());
                    deliverDisturbRuleMapper.insert(deliverDisturbRule);
                }
            }
        }
    }

    private <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object, Boolean> map = new ConcurrentHashMap<>();
        return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    @Override
    public ServiceResult<?> memberDeliverImport(MultipartFile files, String deliverId, boolean isMobile) {
        try {
            ServiceResult<?> resData = isMobile ? memberDeliverImportByMobile(files) : memberDeliverImportByEmail(files);
            if (!resData.isSuccess()) {
                return resData;
            }
            JSONObject data = (JSONObject) resData.getData();
            List<Object> dataList = data.getJSONArray("dataList");
            long filteredLength = dataList.size();
            long uploadLength = data.getLong("uploadLength");
            //log.info("输出：{}", dataList);
            String fileName = "投放名单.xlsx";
            File excelFile = isMobile ? ExcelExportUtil.createExcelStream(dataList, MemberDeliverDataByMobile.class, fileName) :
                    ExcelExportUtil.createExcelStream(dataList, MemberDeliverDataByEmail.class, fileName);
            if (Objects.isNull(deliverId)) {
                deliverId = new ObjectId().toHexString();
            }
            /*String url = ossService.uploadSimpleFile2(excelFile, "投放名单.xlsx", "deliver_member" + SEPARATOR + deliverId + SEPARATOR,
                    DeliverConstant.OSS_TEMP_EXPIRE_SECOND, null, "投放名单", OssPathEnum.TEMP_DELIVER);*/
            String url = storageService.upload(excelFile, "投放名单.xlsx",
                    "deliver_member" + SEPARATOR + deliverId + SEPARATOR, new Dict()).getUrl();
            Map<String, Object> res = new HashMap<>(8);
            res.put("deliver_id", deliverId);
            res.put("quota_balance", 999999999);
            res.put("success", filteredLength + SEPARATOR + uploadLength);
            res.put("xlsx_url", url);
            redisService.set("deliver.member_import." + deliverId, filteredLength + SEPARATOR + uploadLength);
            return ServiceResult.success(res);
        } catch (Exception e) {
            String deliverName = isMobile ? "短信" : "邮件";
            log.error(deliverName + "投放-上传名单读取异常", e);
            return ServiceResult.fail(-1, deliverName + "投放-上传名单读取异常");
        }
    }

    private ServiceResult<?> memberDeliverImportByMobile(MultipartFile files) throws IOException {
        List<MemberDeliverDataByMobile> dataList = EasyExcel.read(files.getInputStream())
                // 设置与Excel表映射的类
                .head(MemberDeliverDataByMobile.class)
                // 设置sheet,默认读取第一个
                .sheet()
                // 设置标题所在行数
                .headRowNumber(1)
                // 同步读取返回结果
                .doReadSync();
        if (dataList.size() > 20000) {
            return ServiceResult.fail(AppResourceErrorCode.TOO_MUCH, "最多上传两万条");
        }
        long uploadLength = dataList.size();
        dataList = dataList.stream().filter(v -> StrUtil.isNotEmpty(v.getName()) && Validator.isMobile(v.getPhone())
                && (StrUtil.isBlank(v.getEmail()) || Validator.isEmail(v.getEmail()))).collect(Collectors.toList());
        JSONObject obj = new JSONObject();
        obj.put("uploadLength", uploadLength);
        obj.put("dataList", dataList);
        return ServiceResult.success(obj);
    }

    private ServiceResult<?> memberDeliverImportByEmail(MultipartFile files) throws IOException {
        List<MemberDeliverDataByEmail> dataList = EasyExcel.read(files.getInputStream())
                // 设置与Excel表映射的类
                .head(MemberDeliverDataByEmail.class)
                // 设置sheet,默认读取第一个
                .sheet()
                // 设置标题所在行数
                .headRowNumber(1)
                // 同步读取返回结果
                .doReadSync();
        if (dataList.size() > 20000) {
            return ServiceResult.fail(AppResourceErrorCode.TOO_MUCH, "最多上传两万条");
        }
        long uploadLength = dataList.size();
        dataList = dataList.stream().filter(v -> StrUtil.isNotEmpty(v.getName()) && Validator.isEmail(v.getEmail()) && (StrUtil.isBlank(v.getPhone())
                || Validator.isMobile(v.getPhone()))).collect(Collectors.toList());
        JSONObject obj = new JSONObject();
        obj.put("uploadLength", uploadLength);
        obj.put("dataList", dataList);
        return ServiceResult.success(obj);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public XmResultJson<?> putDeliversAward(String id, JSONObject param) {
        Map<String, Object> map = new HashMap<>();
        if (ObjectUtil.isEmpty(baseMapper.selectById(id))) {
            return XmResultJson.fail(-1, "Deliver[id=" + id + "] not found");
        }
        //这个mq不改状态，可以不用发
        StatusDto dto = new StatusDto();
        dto.setProjectId(param.getString("pid"));
        deliverMessageSendService.projectUpdated(dto);

        //确认绑定
        if (StrUtil.isNotBlank(param.getString("award_id"))) {
            baseMapper.update(
                    null,
                    Wrappers.<DeliverDeliver>lambdaUpdate()
                            .set(DeliverDeliver::getUpdatedDt, LocalDateTime.now())
                            .set(DeliverDeliver::getAwardId, param.getString("award_id"))
                            .set(DeliverDeliver::getAwardUseType, 1)
                            .eq(DeliverDeliver::getId, id)
            );
        } else {//取消绑定
            baseMapper.update(
                    null,
                    Wrappers.<DeliverDeliver>lambdaUpdate()
                            .set(DeliverDeliver::getUpdatedDt, LocalDateTime.now())
                            .set(DeliverDeliver::getAwardId, null)
                            .set(DeliverDeliver::getAwardUseType, 0)
                            .eq(DeliverDeliver::getId, id)
            );
        }

        map.put("id", id);
        return XmResultJson.success(map);
    }

    @Override
    public void getMemberRespMaps(String project_id, String survey_resp_status_arg, String deliver_id, Integer check_submit, Map<String, Integer> member_resp_maps, List<String> member_ids) {
        //python每次取3000个调用一次grpc
        List<List<String>> mem_ids_list = ListUtil.partition(member_ids, 3000);
        if (check_submit.equals(0)) {
            for (List<String> mem_ids : mem_ids_list) {
                List<JSONObject> respondent_list = surveyGrpcClient.getDeliverRspdAmountBatch(deliver_id, mem_ids, RespondentStatusEnum.RESPONDENT_STATUS_FINISH.getValue(), project_id);
                Map<String, Integer> tmp = respondent_list
                        .stream()
                        .collect(Collectors.toMap(e -> e.getString("member_id"), e -> e.getInteger("amount"), (a, b) -> a));
                member_resp_maps.putAll(tmp);
                for (String it : mem_ids) {
                    if (!member_resp_maps.containsKey(it)) {
                        member_resp_maps.put(it, 0);
                    }
                }
            }
        } else {
            for (List<String> mem_ids : mem_ids_list) {
                if (String.valueOf(RespondentStatusEnum.RESPONDENT_STATUS_NOTSTART.getValue()).equals(survey_resp_status_arg)) {
                    List<JSONObject> respondent_list = surveyGrpcClient.getDeliverRspdStatusBatch(deliver_id, mem_ids, null, project_id);
                    List<String> resp_member_id_list = respondent_list.stream().map(it -> it.getString("member_id")).collect(Collectors.toList());

                    fillNoStart(member_resp_maps, mem_ids, resp_member_id_list);
                } else {
                    List<JSONObject> respondent_list = surveyGrpcClient.getDeliverRspdStatusBatch(deliver_id, mem_ids, survey_resp_status_arg, project_id);
                    List<String> resp_member_id_list = new ArrayList<>();
                    if (StrUtil.isNotBlank(survey_resp_status_arg)) {
                        for (JSONObject resp : respondent_list) {
                            String rstatus = resp.getString("status");
                            if (rstatus.equals(survey_resp_status_arg) && RESPONDENT_STATUS.contains(rstatus)) {
                                member_resp_maps.put(resp.getString("member_id"), Integer.valueOf(rstatus));
                            }
                        }
                    } else {
                        for (JSONObject resp : respondent_list) {
                            String rstatus = resp.getString("status");
                            String member_id = resp.getString("member_id");
                            resp_member_id_list.add(member_id);
                            if (RESPONDENT_STATUS.contains(rstatus)) {
                                member_resp_maps.put(member_id, Integer.valueOf(rstatus));
                            }
                        }
                        fillNoStart(member_resp_maps, mem_ids, resp_member_id_list);
                    }
                }
            }
        }
    }

    @Override
    public void fillNoStart(Map<String, Integer> member_resp_maps, List<String> mem_ids, List<String> resp_member_id_list) {
        HashSet<String> set1 = new HashSet<>(mem_ids);
        HashSet<String> set2 = new HashSet<>(resp_member_id_list);
        List<String> nostart_member_id_list = new ArrayList<>(org.apache.commons.collections4.CollectionUtils.subtract(set1, set2));
        for (String member_id : nostart_member_id_list) {
            member_resp_maps.put(member_id, RespondentStatusEnum.RESPONDENT_STATUS_NOTSTART.getValue());
        }
    }

    @Override
    public List<?> getTimeRangeSurveyIdList(Integer delayTime, String filterType) {
        // 因貌似不支持search参数，所以将orgId组织到project中
        ProjectNoDefault project = ProjectNoDefault.newBuilder().setCompanyId(StringValue.of(SecurityContextHolder.getOrgId())).build();
        List<Project> projectList = qdesGrpcClient.listProjectNoDefault(project);
        if (projectList.isEmpty()) {
            return Collections.emptyList();
        }
        projectList = projectList.stream().filter(v -> v.getStatus() != PROJECT_STATUS_DELETED && v.getStatus() != ProjectStatus.PROJECT_STATUS_DELETED2).sorted(Comparator.comparing(Project::getUpdatedDt, Comparator.reverseOrder())).collect(Collectors.toList());
        LocalDateTime timeRangeEnd = LocalDateTime.now();
        LocalDateTime timeRangeStart = timeRangeEnd.minusDays(delayTime);
        List<Map<String, String>> info = new ArrayList<>();
        if ("commit".equals(filterType)) {
            projectList.forEach(v -> {
                List<String> responseList = surveyGrpcClient.getFinishedAllMemberIds(v.getId(), LocalDateTimeUtil.toEpochMilli(timeRangeStart), LocalDateTimeUtil.toEpochMilli(timeRangeEnd));
                if (CollectionUtil.isNotEmpty(responseList)) {
                    Map<String, String> data = new HashMap<>();
                    data.put("title", v.getTitle());
                    data.put("id", v.getId());
                    info.add(data);
                }
            });
        } else {
            LambdaQueryWrapper<DeliverSurveyDeliver> queryWrapper = new LambdaQueryWrapper<>();
            projectList.forEach(v -> {
                queryWrapper.eq(DeliverSurveyDeliver::getSurveyId, v.getId());
                queryWrapper.ge(DeliverSurveyDeliver::getCreatedDt, timeRangeStart);
                queryWrapper.le(DeliverSurveyDeliver::getCreatedDt, timeRangeEnd);
                List<DeliverSurveyDeliver> deliverSurveyDelivers = deliverSurveyDeliverMapper.selectList(queryWrapper);
                for (DeliverSurveyDeliver surveyDeliver : deliverSurveyDelivers) {
                    DeliverDeliver deliver = baseMapper.selectById(surveyDeliver.getDeliverId());
                    if (deliver.getChannel() != DELIVER_CHANNEL_QRCODE.getValue() && DeliverStatusEnum.deliverNormalStatus().contains(deliver.getStatus())) {
                        Map<String, String> data = new HashMap<>();
                        data.put("title", v.getTitle());
                        data.put("id", v.getId());
                        info.add(data);
                        break;
                    }
                }
            });
        }
        log.info("response_info---{}", info);
        return info;
    }

    @Override
    public Object surveyDetail(DeliverDeliver deliver, SurveyDetailParam param) {
        String member_id = param.getMemberId();
        String deliver_id = param.getDeliverId();
        JSONObject query_params = new JSONObject();
        JSONObject filter_params = new JSONObject();
        filter_params.put("member_id", member_id);
        if (deliver.getTtype().equals(DELIVER_TTYPE_SMS.getValue())) {
            QueryWrapper<DeliverSmsDeliver> wrapper = new QueryWrapper<>();
            wrapper.select("deliver_group_id");
            wrapper.eq("deliver_id", deliver.getId());
            List<Object> idsObj = smsDeliverMapper.selectObjs(wrapper);
            setParams(deliver_id, query_params, filter_params, idsObj);
        } else if (deliver.getTtype().equals(DELIVER_TTYPE_EMAIL.getValue())) {
            QueryWrapper<DeliverEmailDeliver> wrapper = new QueryWrapper<>();
            wrapper.select("deliver_group_id");
            wrapper.eq("deliver_id", deliver.getId());
            List<Object> idsObj = emailDeliverMapper.selectObjs(wrapper);
            setParams(deliver_id, query_params, filter_params, idsObj);
        } else if (deliver.getTtype().equals(DELIVER_TTYPE_CUSTOM_API.getValue())) {
            QueryWrapper<DeliverCustomApiDeliver> wrapper = new QueryWrapper<>();
            wrapper.select("deliver_group_id");
            wrapper.eq("deliver_id", deliver.getId());
            List<Object> idsObj = customApiDeliverMapper.selectObjs(wrapper);
            setParams(deliver_id, query_params, filter_params, idsObj);
        } else if (deliver.getTtype().equals(DELIVER_TTYPE_WECHATMP.getValue())) {
            query_params.put("deliver_id", deliver_id);
            filter_params.put("deliver_id", deliver_id);
        }
        query_params.put("member_id", member_id);
        query_params.put("project_id", param.getProjectId());
        log.info("答卷详情获取参数-----{}", query_params.toJSONString());
        List<SurveyRespondentDto> respondents = surveyGrpcClient.listRespondent(query_params);
        log.info("获取到的答卷长度:{}", respondents.size());
        int status = RESPONDENT_STATUS_BEGIN.getValue();

        for (SurveyRespondentDto resp : respondents) {
            //这一坨没找到oid传值
            //if resp.dlvr_rid and oid and resp.dlvr_rid != oid:
            //continue
            if (resp.getStatus().equals(RespondentStatusEnum.RESPONDENT_STATUS_FINISH.getValue())) {
                status = DELIVERSURVEYDATA_SURVEY_STATUS_SUBMIT.getValue();
            }
            resp.setDeliverName(deliver.getName());
        }
        int survey_nums = respondents.size();

        //更新投放问卷数据
        saveDeliverSurveyData(filter_params, survey_nums, status);
        Map<String, Object> ret = new HashMap<>();
        ret.put("total_count", survey_nums);
        ret.put("rows", respondents);
        ret.put("deliver_time", LocalDateTimeUtil.formatNormal(deliver.getCreatedDt()));
        ret.put("page", 1);

        //try {
        //    final String s = objectMapper.writeValueAsString(respondents);
        //    log.info(s);
        //} catch (JsonProcessingException e) {
        //    throw new RuntimeException(e);
        //}
        return ret;
    }

    private void saveDeliverSurveyData(JSONObject filter_params, int survey_nums, int survey_status) {
        LambdaQueryWrapper<DeliverDeliverSurveyData> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(StrUtil.isNotBlank(filter_params.getString("member_id")), DeliverDeliverSurveyData::getMemberId, filter_params.getString("member_id"));
        queryWrapper.eq(StrUtil.isNotBlank(filter_params.getString("deliver_id")), DeliverDeliverSurveyData::getDeliverId, filter_params.getString("deliver_id"));
        queryWrapper.eq(StrUtil.isNotBlank(filter_params.getString("deliver_group_id")), DeliverDeliverSurveyData::getDeliverGroupId, filter_params.getString("deliver_group_id"));
        queryWrapper.last("limit 1");
        DeliverDeliverSurveyData d_s_data = deliverSurveyDataMapper.selectOne(queryWrapper);
        if (Objects.isNull(d_s_data)) {
            DeliverDeliverSurveyData item = new DeliverDeliverSurveyData();
            item.setDeliverGroupId(filter_params.getString("deliver_group_id"));
            item.setDeliverId(filter_params.getString("deliver_id"));
            item.setMemberId(filter_params.getString("member_id"));
            item.setUrgeNums(0);
            item.setSurveyNums(0);
            item.setSurveyStatus(DELIVERSURVEYDATA_SURVEY_STATUS_UNANSWER.getValue());
            deliverSurveyDataMapper.insert(item);
            d_s_data = item;
        }
        Boolean update_d_s_data = Boolean.FALSE;
        if (!d_s_data.getSurveyStatus().equals(DELIVERSURVEYDATA_SURVEY_STATUS_SUBMIT.getValue())) {
            //收集的问卷数为0， 状态不进行更改
            if (DELIVERSURVEYDATA_SURVEY_STATUS_LIST.contains(survey_status) && survey_nums > 0) {
                update_d_s_data = Boolean.TRUE;
                d_s_data.setSurveyStatus(survey_status);
            }
        }

        if (d_s_data.getSurveyNums() == null || d_s_data.getSurveyNums().equals(0) || d_s_data.getSurveyNums() < survey_nums) {
            update_d_s_data = Boolean.TRUE;
            d_s_data.setSurveyNums(survey_nums);
        }

        if (update_d_s_data) {
            deliverSurveyDataMapper.updateById(d_s_data);
        }
    }

    private static void setParams(String deliver_id, JSONObject query_params, JSONObject filter_params, List<Object> idsObj) {
        if (!org.springframework.util.CollectionUtils.isEmpty(idsObj) && Objects.nonNull(idsObj.get(0))) {
            String deliver_group_id = idsObj.get(0).toString();
            query_params.put("deliver_group_id", deliver_group_id);
            filter_params.put("deliver_group_id", deliver_group_id);
        } else {
            query_params.put("deliver_group_id", "");
            query_params.put("deliver_id", deliver_id);
            filter_params.put("deliver_id", deliver_id);
        }
    }
}